station data fetch successfully

This commit is contained in:
armiejean 2025-05-19 11:50:42 +08:00
parent 9e5a466a5b
commit 9809ef792a
5 changed files with 267 additions and 205 deletions

View File

@ -4,11 +4,75 @@ namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Session;
class ReportsController extends Controller
{
protected $apiBaseUrl = 'http://192.168.100.6:8081/api/';
protected $apiBaseUrl;
public function __construct()
{
$this->apiBaseUrl = env('EXTERNAL_API_URL', 'http://localhost:8081/api/');
}
private function makeApiRequest($endpoint, $params)
{
try {
$user = Session::get('user');
$accessToken = $user['access_token'] ?? null;
if (!$accessToken) {
Log::info("No access token found, redirecting to login from {$endpoint}");
return redirect()->route('login')->with('error', 'Please log in to view reports.');
}
// Ensure proper URL formation
$baseUrl = rtrim($this->apiBaseUrl, '/');
$endpoint = ltrim($endpoint, '/');
$fullUrl = "{$baseUrl}/{$endpoint}";
Log::info("Making API request to: {$fullUrl}", ['params' => $params]);
$response = Http::withHeaders([
'Accept' => 'application/json',
'Authorization' => 'Bearer ' . $accessToken,
])->timeout(30)->get($fullUrl, $params);
Log::info("API response status: {$response->status()}", [
'endpoint' => $endpoint,
'response_body' => $response->body(),
'headers' => $response->headers()
]);
if ($response->status() === 401 || $response->status() === 403) {
Log::warning("Unauthorized or Forbidden API response for {$endpoint}", ['response' => $response->json()]);
return redirect()->route('login')->with('error', 'Your session has expired. Please log in again.');
}
if (!$response->successful()) {
Log::error("API request failed for {$endpoint}", [
'status' => $response->status(),
'response' => $response->body(),
'headers' => $response->headers()
]);
return null;
}
return $response->json();
} catch (\Illuminate\Http\Client\ConnectionException $e) {
Log::error("Connection error during API request to {$endpoint}", [
'error' => $e->getMessage(),
'params' => $params
]);
return null;
} catch (\Exception $e) {
Log::error("Unexpected error during API request to {$endpoint}", [
'error' => $e->getMessage(),
'params' => $params
]);
return null;
}
}
public function mobileUsage(Request $request)
{
$params = [
@ -17,17 +81,22 @@ class ReportsController extends Controller
'date_start' => $request->input('date_start'),
'date_end' => $request->input('date_end'),
'sorting' => $request->input('sort', 'date|desc'),
'_search' => $request->input('_search'),
];
$response = Http::get($this->apiBaseUrl . 'cms/reportMobileUsage', $params);
$data = $response->json();
$data = $this->makeApiRequest('cms/reportMobileUsage', $params);
if (is_a($data, '\Illuminate\Http\RedirectResponse')) {
return $data; // Redirect to login if unauthorized
}
$mobileUsageData = [];
$currentPage = $params['page'];
$lastPage = 1;
$total = 0;
$errorMessage = null;
if ($response->successful() && isset($data['data'])) {
if ($data && isset($data['data'])) {
$mobileUsageData = array_map(function ($item) {
return [
'id' => $item['id'] ?? uniqid(),
@ -40,9 +109,15 @@ class ReportsController extends Controller
$currentPage = $data['meta']['current_page'] ?? 1;
$lastPage = $data['meta']['last_page'] ?? 1;
$total = $data['meta']['total'] ?? count($mobileUsageData);
} elseif ($data && isset($data['message'])) {
Log::info("API returned message for mobileUsage", ['message' => $data['message']]);
$errorMessage = $data['message'];
} else {
Log::warning("No data returned or invalid response structure for mobileUsage", ['data' => $data]);
$errorMessage = 'Failed to fetch mobile usage data.';
}
return view('pages.reports.mobile-usage-report', compact('mobileUsageData', 'currentPage', 'lastPage', 'total'));
return view('pages.reports.mobile-usage-report', compact('mobileUsageData', 'currentPage', 'lastPage', 'total', 'errorMessage'));
}
public function registration(Request $request)
@ -53,17 +128,22 @@ class ReportsController extends Controller
'date_start' => $request->input('date_start'),
'date_end' => $request->input('date_end'),
'sorting' => $request->input('sort', 'date|desc'),
'_search' => $request->input('_search'),
];
$response = Http::get($this->apiBaseUrl . 'cms/reportRegistration', $params);
$data = $response->json();
$data = $this->makeApiRequest('cms/reportRegistration', $params);
if (is_a($data, '\Illuminate\Http\RedirectResponse')) {
return $data;
}
$registrationData = [];
$currentPage = $params['page'];
$lastPage = 1;
$total = 0;
$errorMessage = null;
if ($response->successful() && isset($data['data'])) {
if ($data && isset($data['data'])) {
$registrationData = array_map(function ($item) {
return [
'id' => $item['id'] ?? uniqid(),
@ -75,48 +155,64 @@ class ReportsController extends Controller
$currentPage = $data['meta']['current_page'] ?? 1;
$lastPage = $data['meta']['last_page'] ?? 1;
$total = $data['meta']['total'] ?? count($registrationData);
} elseif ($data && isset($data['message'])) {
Log::info("API returned message for registration", ['message' => $data['message']]);
$errorMessage = $data['message'];
} else {
Log::warning("No data returned or invalid response structure for registration", ['data' => $data]);
$errorMessage = 'Failed to fetch registration data.';
}
return view('pages.reports.registration-report', compact('registrationData', 'currentPage', 'lastPage', 'total'));
return view('pages.reports.registration-report', compact('registrationData', 'currentPage', 'lastPage', 'total', 'errorMessage'));
}
public function stationRating(Request $request)
{
{
$params = [
'page' => $request->input('page', 1),
'page_size' => 5,
'date_start' => $request->input('date_start'),
'date_end' => $request->input('date_end'),
'sorting' => $request->input('sort', 'transaction_date_time|desc'),
'sorting' => $request->input('sort', 'date|desc'),
'_search' => $request->input('_search'),
];
$response = Http::get($this->apiBaseUrl . 'cms/reportStationRatings', $params);
$data = $response->json();
$data = $this->makeApiRequest('cms/reportStationRatings', $params);
if (is_a($data, '\Illuminate\Http\RedirectResponse')) {
return $data;
}
$stationRatingData = [];
$currentPage = $params['page'];
$lastPage = 1;
$total = 0;
$errorMessage = null;
if ($response->successful() && isset($data['data'])) {
if ($data && isset($data['data'])) {
$stationRatingData = array_map(function ($item) {
return [
'id' => $item['id'] ?? uniqid(),
'transactionDateTime' => $item['transaction_date_time'],
'transactionDateTime' => $item['date'],
'cardNumber' => $item['card_number'],
'salesInvoice' => $item['sales_invoice'],
'salesInvoice' => $item['invoice'],
'station' => $item['station'],
'ratings' => $item['ratings'],
'ratings' => $item['rate'],
];
}, $data['data']);
$currentPage = $data['meta']['current_page'] ?? 1;
$lastPage = $data['meta']['last_page'] ?? 1;
$total = $data['meta']['total'] ?? count($stationRatingData);
} elseif ($data && isset($data['message'])) {
Log::info("API returned message for stationRating", ['message' => $data['message']]);
$errorMessage = $data['message'];
} else {
Log::warning("No data returned or invalid response structure for stationRating", ['data' => $data]);
$errorMessage = 'Failed to fetch station rating data.';
}
return view('pages.reports.station-rating-report', compact('stationRatingData', 'currentPage', 'lastPage', 'total'));
}
return view('pages.reports.station-rating-report', compact('stationRatingData', 'currentPage', 'lastPage', 'total', 'errorMessage'));
}
public function topUp(Request $request)
{
$params = [
@ -125,17 +221,22 @@ class ReportsController extends Controller
'date_start' => $request->input('date_start'),
'date_end' => $request->input('date_end'),
'sorting' => $request->input('sort', 'transaction_date_time|desc'),
'_search' => $request->input('_search'),
];
$response = Http::get($this->apiBaseUrl . 'cms/reportTopUp', $params);
$data = $response->json();
$data = $this->makeApiRequest('cms/reportTopUp', $params);
if (is_a($data, '\Illuminate\Http\RedirectResponse')) {
return $data;
}
$topUpData = [];
$currentPage = $params['page'];
$lastPage = 1;
$total = 0;
$errorMessage = null;
if ($response->successful() && isset($data['data'])) {
if ($data && isset($data['data'])) {
$topUpData = array_map(function ($item) {
return [
'id' => $item['id'] ?? uniqid(),
@ -147,8 +248,14 @@ class ReportsController extends Controller
$currentPage = $data['meta']['current_page'] ?? 1;
$lastPage = $data['meta']['last_page'] ?? 1;
$total = $data['meta']['total'] ?? count($topUpData);
} elseif ($data && isset($data['message'])) {
Log::info("API returned message for topUp", ['message' => $data['message']]);
$errorMessage = $data['message'];
} else {
Log::warning("No data returned or invalid response structure for topUp", ['data' => $data]);
$errorMessage = 'Failed to fetch top-up data.';
}
return view('pages.reports.top-up-usage-report', compact('topUpData', 'currentPage', 'lastPage', 'total'));
return view('pages.reports.top-up-usage-report', compact('topUpData', 'currentPage', 'lastPage', 'total', 'errorMessage'));
}
}

View File

@ -11,7 +11,7 @@ class StationController extends Controller
protected $apiBaseUrl = 'http://192.168.100.6:8081/api/';
public function index(Request $request)
{
{
$params = [
'page' => $request->input('page', 1),
'page_size' => 5,
@ -22,6 +22,8 @@ class StationController extends Controller
$response = Http::get($this->apiBaseUrl . 'cms/getStations', $params);
$data = $response->json();
\Log::info('Stations API Response:', ['status' => $response->status(), 'data' => $data]);
$stations = [];
$currentPage = $params['page'];
$lastPage = 1;
@ -33,20 +35,18 @@ class StationController extends Controller
'id' => $item['station_uuid'],
'station_code' => $item['code'],
'station_name' => $item['description'],
'branch_name' => 'N/A', // Mocked; replace with actual API field if available
'date_created' => 'N/A', // Mocked
'created_by' => 'N/A', // Mocked
'modified_by' => 'N/A', // Mocked
'date_modified' => 'N/A', // Mocked
'branch_name' => 'N/A',
'date_created' => 'N/A',
'created_by' => 'N/A',
'modified_by' => 'N/A',
'date_modified' => 'N/A',
];
}, $data['data']);
// Mock pagination since API may not support it
$total = count($data['data']);
$lastPage = ceil($total / $params['page_size']);
$currentPage = min($currentPage, $lastPage);
// Handle search client-side if API doesn't support _search
if ($params['_search']) {
$searchTerm = strtolower($params['_search']);
$stations = array_filter($stations, function ($station) use ($searchTerm) {
@ -59,7 +59,6 @@ class StationController extends Controller
$lastPage = ceil($total / $params['page_size']);
}
// Handle sorting client-side if API doesn't support sort
if ($params['sort']) {
[$sortField, $sortDir] = explode('|', $params['sort']);
usort($stations, function ($a, $b) use ($sortField, $sortDir) {
@ -69,13 +68,12 @@ class StationController extends Controller
});
}
// Paginate manually
$start = ($currentPage - 1) * $params['page_size'];
$stations = array_slice($stations, $start, $params['page_size']);
}
return view('pages.station locator.stations', compact('stations', 'currentPage', 'lastPage', 'total'));
}
}
// public function create()
// {

View File

@ -14,7 +14,6 @@
'total' => 0,
])
<div class="card-body">
<!-- Filters -->
<div class="d-flex justify-content-between mb-3 flex-wrap gap-2">
@ -26,23 +25,10 @@
<span class="input-group-text bg-light border-end-0">
<i class="fa-solid fa-magnifying-glass text-muted"></i>
</span>
<input type="text" class="form-control border-start-0" placeholder="Search..." id="searchInput" data-search-param="_search">
<input type="text" class="form-control border-start-0" placeholder="Search..." id="searchInput" data-search-param="_search" value="{{ request('_search') }}">
</div>
</div>
@endif
{{-- <div class="d-flex flex-column">
<label for="startDate" class="form-label mb-1">Start Date</label>
<div class="input-group">
<span class="input-group-text"><i class="fas fa-calendar-alt"></i></span>
<input type="date" class="form-control" id="startDate">
</div>
</div> --}}
{{-- <div class="d-flex flex-column">
<label for="endDate" class="form-label mb-1">End Date</label>
<div class="input-group">
<input type="date" class="form-control" id="endDate">
</div>
</div> --}}
</div>
<div class="d-flex gap-2 align-items-end">
<button class="btn btn-outline-secondary btn-sm" id="clearFilters">
@ -51,6 +37,11 @@
</div>
</div>
<!-- No Data Message -->
@if (empty($data))
<div class="alert alert-info" id="no-data-message">No data available.</div>
@endif
<!-- Table -->
<div class="table-container">
<table class="table table-hover align-middle">
@ -127,6 +118,8 @@
exportEndpoint: '{{ route( Str::kebab(str_replace(" ", "-", strtolower($pageTitle)))) }}Export',
};
console.log('Table Config:', tableConfig); // Log initial config
const rowsPerPage = 5;
let currentPage = tableConfig.currentPage;
let filteredRows = [...tableConfig.data];
@ -154,14 +147,23 @@
}
function renderTable() {
console.log('Rendering table with filteredRows:', filteredRows); // Log data
const tableBody = document.getElementById('tableBody');
if (!tableBody) return;
if (!tableBody) {
console.error('Table body not found');
return;
}
tableBody.innerHTML = '';
if (filteredRows.length === 0) {
tableBody.innerHTML = `<tr><td colspan="${tableConfig.columns.length}">No data to display</td></tr>`;
console.log('No data to render');
return;
}
filteredRows.forEach(row => {
const tr = document.createElement('tr');
let rowHtml = '';
tableConfig.columns.forEach(col => {
let value = row[col.key] || '';
if (col.format === 'date' || col.key.includes('DateTime')) {
@ -169,17 +171,18 @@
}
rowHtml += `<td>${value}</td>`;
});
tr.innerHTML = rowHtml;
tableBody.appendChild(tr);
});
updateNoDataMessage();
}
function renderPagination() {
console.log('Rendering pagination:', { currentPage, lastPage: tableConfig.lastPage }); // Log pagination
const pagination = document.getElementById('pagination');
if (!pagination) return;
if (!pagination) {
console.error('Pagination element not found');
return;
}
pagination.innerHTML = '';
const routeBase = tableConfig.pageTitle.toLowerCase().replace(/\s+/g, '-');
@ -222,6 +225,7 @@
}
function updateUrl(page, search, startDate, endDate) {
console.log('Updating URL:', { page, search, startDate, endDate, sortColumn, sortDirection }); // Log URL update
const url = new URL(window.location);
url.searchParams.set('page', page);
if (search) url.searchParams.set('_search', search);
@ -238,17 +242,11 @@
window.location.href = url.toString();
}
function updateNoDataMessage() {
const noDataMessage = document.getElementById('no-data-message');
if (noDataMessage) {
noDataMessage.style.display = filteredRows.length === 0 ? 'block' : 'none';
}
}
function attachEventListeners() {
const searchInput = document.getElementById('searchInput');
if (searchInput) {
searchInput.addEventListener('input', function() {
console.log('Search input changed:', this.value); // Log search
const url = new URL(window.location);
const searchTerm = this.value;
if (searchTerm) {
@ -266,6 +264,7 @@
const columnKey = this.getAttribute('data-key');
sortDirection = sortColumn === columnKey && sortDirection === 'asc' ? 'desc' : 'asc';
sortColumn = columnKey;
console.log('Sorting:', { columnKey, sortDirection }); // Log sorting
document.querySelectorAll('.sortable i').forEach(icon => {
icon.classList.remove('fa-sort-up', 'fa-sort-down');
@ -286,13 +285,20 @@
const startDateInput = document.getElementById('startDate');
const endDateInput = document.getElementById('endDate');
if (startDateInput && endDateInput) {
startDateInput.addEventListener('change', () => updateUrl(1, document.getElementById('searchInput')?.value || '', startDateInput.value, endDateInput.value));
endDateInput.addEventListener('change', () => updateUrl(1, document.getElementById('searchInput')?.value || '', startDateInput.value, endDateInput.value));
startDateInput.addEventListener('change', () => {
console.log('Start date changed:', startDateInput.value); // Log date
updateUrl(1, document.getElementById('searchInput')?.value || '', startDateInput.value, endDateInput.value);
});
endDateInput.addEventListener('change', () => {
console.log('End date changed:', endDateInput.value); // Log date
updateUrl(1, document.getElementById('searchInput')?.value || '', startDateInput.value, endDateInput.value);
});
}
const clearFilters = document.getElementById('clearFilters');
if (clearFilters) {
clearFilters.addEventListener('click', function() {
console.log('Clearing filters'); // Log filter clear
if (searchInput) searchInput.value = '';
if (startDateInput) startDateInput.value = '';
if (endDateInput) endDateInput.value = '';
@ -309,6 +315,7 @@
const exportCsvBtn = document.getElementById('exportCsv');
if (exportCsvBtn) {
exportCsvBtn.addEventListener('click', () => {
console.log('Exporting CSV'); // Log export
const url = new URL(tableConfig.exportEndpoint, window.location.origin);
const searchTerm = document.getElementById('searchInput')?.value || '';
const startDate = document.getElementById('startDate')?.value || '';
@ -319,6 +326,7 @@
if (sortColumn && sortDirection) {
url.searchParams.set('sort', `${sortColumn}|${sortDirection}`);
}
console.log('Export URL:', url.toString()); // Log export URL
window.location.href = url.toString();
});
}

View File

@ -8,6 +8,11 @@
</div>
<div class="row justify-content-center">
<div class="card-body p-3">
<!-- Error Message -->
@if ($errorMessage)
<div class="alert alert-danger">{{ $errorMessage }}</div>
@endif
<!-- Filters -->
<div class="d-flex justify-content-between mb-3 flex-wrap gap-2">
<div class="d-flex align-items-center gap-2">
@ -15,13 +20,13 @@
<label for="startDate" class="form-label mb-1">Start Date</label>
<div class="input-group">
<span class="input-group-text"><i class="fas fa-calendar-alt"></i></span>
<input type="date" class="form-control" id="startDate">
<input type="date" class="form-control" id="startDate" value="{{ request('date_start') }}">
</div>
</div>
<div class="d-flex flex-column">
<label for="endDate" class="form-label mb-1">End Date</label>
<div class="input-group">
<input type="date" class="form-control" id="endDate">
<input type="date" class="form-control" id="endDate" value="{{ request('date_end') }}">
</div>
</div>
</div>
@ -46,7 +51,7 @@
'showAddButton' => false,
'showCheckboxes' => false,
'showBatchDelete' => false,
'showSearch' => false,
'showSearch' => true,
'currentPage' => $currentPage ?? 1,
'lastPage' => $lastPage ?? 1,
'total' => $total ?? 0,
@ -55,20 +60,6 @@
</div>
<style>
.card {
border-radius: 5px;
border: 1px solid #dee2e6;
}
.form-control {
font-size: 0.9rem;
}
.btn-outline-secondary {
border-color: #6c757d;
color: #6c757d;
}
.btn-outline-secondary:hover {
background-color: #f8f9fa;
}
.btn-export-csv {
background-color: #ff6200;
color: white;
@ -77,64 +68,22 @@
.btn-export-csv:hover {
background-color: #e65a00;
}
.input-group-text {
background-color: #f8f9fa;
}
</style>
<script>
let originalData = [...tableConfig.data];
const startDateInput = document.getElementById('startDate');
const endDateInput = document.getElementById('endDate');
const exportCsvBtn = document.getElementById('exportCsv');
function filterData() {
const startDate = startDateInput.value ? new Date(startDateInput.value) : null;
const endDate = endDateInput.value ? new Date(endDateInput.value) : null;
tableConfig.data = originalData.filter(item => {
const itemDate = new Date(item.transactionDateTime);
if (startDate && itemDate < startDate) return false;
if (endDate) {
const endDateAdjusted = new Date(endDate);
endDateAdjusted.setHours(23, 59, 59, 999);
if (itemDate > endDateAdjusted) return false;
}
return true;
});
currentPage = 1;
renderTable();
renderPagination();
}
startDateInput.addEventListener('change', filterData);
endDateInput.addEventListener('change', filterData);
exportCsvBtn.addEventListener('click', () => {
const headers = tableConfig.columns.map(col => col.name).join(',');
const rows = tableConfig.data.map(item => {
return [
item.transactionDateTime,
item.cardNumber,
item.salesInvoice,
item.station,
item.ratings
].join(',');
const url = new URL(tableConfig.exportEndpoint, window.location.origin);
const searchTerm = document.getElementById('searchInput')?.value || '';
const startDate = document.getElementById('startDate')?.value || '';
const endDate = document.getElementById('endDate')?.value || '';
if (searchTerm) url.searchParams.set('_search', searchTerm);
if (startDate) url.searchParams.set('date_start', startDate);
if (endDate) url.searchParams.set('date_end', endDate);
if (sortColumn && sortDirection) {
url.searchParams.set('sort', `${sortColumn}|${sortDirection}`);
}
window.location.href = url.toString();
});
const csvContent = [headers, ...rows].join('\n');
const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' });
const link = document.createElement('a');
const url = URL.createObjectURL(blob);
link.setAttribute('href', url);
link.setAttribute('download', 'station-rating-report.csv');
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
});
// Initial render
renderTable();
renderPagination();
</script>
@endsection

View File

@ -3,7 +3,7 @@
@section('page_title', 'Station Management')
@section('content')
@include('components.station-component', [
@include('components.table-component', [
'pageTitle' => 'Station Management',
'data' => $stations ?? [],
'columns' => [