diff --git a/src/app/transaction-logs/transaction-logs.component.html b/src/app/transaction-logs/transaction-logs.component.html index cd98bc0..e769eea 100644 --- a/src/app/transaction-logs/transaction-logs.component.html +++ b/src/app/transaction-logs/transaction-logs.component.html @@ -1,5 +1,6 @@
+
@@ -7,177 +8,133 @@
- +
+
-
-
-
-
-
-
- {{ "transactionLogs" | translate }} +
+ {{ "transactionLogs" | translate }} +
-
- - - - - -
- -
+
+
+
+ +
+ + +
+
+ {{ 'fieldRequired' | translate }} +
+
- -
-
+
+ +
+ + +
+
+ {{ 'fieldRequired' | translate }} +
+
+ {{ 'toDateInvalidError' | translate }} +
+
+
- -
-
-
- - -
-
- - -
-
-
- -
- - - -
- - - -
-
-
- - -
- - - {{ "activeFilter" | translate }}: - {{ "from" | translate }} {{ fromDate | date }} - {{ "to" | translate }} - {{ toDate | date }} - -
-
+
+ +
+ +
+
+
-
- -
-
- Loading... -
-

{{ "loadingTransactionLogs" | translate }}

- - {{ "filteringByDate" | translate }} - -
+ +
+
+
+
+ {{ "transactionLogDetails" | translate }} - - +
+ - -
- -
{{ "noTransactionLogsFound" | translate }}
-

- {{ "tryAdjustingFilters" | translate }} -

-
+
+ +
+ + +
+
+
+ +
+ +
+ +

{{ "noTransactionLogsFound" | translate }}

+
- -
- - - - + +
+
{{ "logID" | translate }}
+ + + @@ -190,10 +147,11 @@ - - - - {{ log.channelCode || '-' }} - -
{{ "logID" | translate }} {{ "organization" | translate }} {{ "transactionID" | translate }} {{ "drAccount" | translate }}{{ "date" | translate }} {{ "channel" | translate }} {{ "createdAt" | translate }}
{{ log.createdAt | date: 'MMM dd, yyyy HH:mm' }}
+ + - -
- -
- {{ "show" | translate }} -
- - - {{ item.value }} {{ "entries" | translate }} - + +
+ +
+ {{ "show" | translate }} +
+ + + {{ item.value }} {{ "entries" | translate }} + - - {{ item.value }} {{ "entries" | translate }} - - -
-
+ + {{ item.value }} {{ "entries" | translate }} + + +
+
- -
- {{ "page" | translate }} {{ currentPage }} - {{ "of" | translate }} {{ totalPages() }} - - ({{ totalCount }} {{ "totalItems" | translate }}) - - - - -
+ +
+ {{ "page" | translate }} {{ currentPage }} + {{ "of" | translate }} {{ totalPages() }} + + ({{ filteredItems.length }} {{ "totalItems" | translate }}) + +
- -
- - -
-
-
-
-
-
+ +
+ +
@@ -297,14 +245,4 @@
-
- - -
- -

{{ "tableCollapsed" | translate }}

- -
-
\ No newline at end of file +
\ No newline at end of file diff --git a/src/app/transaction-logs/transaction-logs.component.scss b/src/app/transaction-logs/transaction-logs.component.scss index 7767147..a4a46b7 100644 --- a/src/app/transaction-logs/transaction-logs.component.scss +++ b/src/app/transaction-logs/transaction-logs.component.scss @@ -1,139 +1,29 @@ -// Date filter section -.date-filter-section { - transition: all 0.3s ease; +.date-input-wrapper { + position: relative; - .date-input-group { - position: relative; - - .form-control { - padding-right: 2.5rem; - } + input[type="date"] { + cursor: pointer; + padding-right: 35px; - .date-icon { + &::-webkit-calendar-picker-indicator { position: absolute; - right: 0.75rem; - top: 50%; - transform: translateY(-50%); - color: #6c757d; - } - } - - .quick-filter-btn { - &.active { - background-color: var(--bs-primary); - color: white; - border-color: var(--bs-primary); - } - } -} - -// Table styling -.table-responsive { - min-height: 400px; - - table { - font-size: 0.875rem; - - th { - font-weight: 600; - white-space: nowrap; - background-color: #f8f9fa; - } - - td { - vertical-align: middle; - - code { - font-size: 0.75rem; - background-color: #f8f9fa; - padding: 0.2rem 0.4rem; - border-radius: 0.25rem; - } - - .badge { - font-size: 0.7rem; - padding: 0.25rem 0.5rem; - } + left: 0; + top: 0; + width: 100%; + height: 100%; + margin: 0; + padding: 0; + cursor: pointer; + opacity: 0; } } -} - -// Loading spinner -.spinner-border { - width: 3rem; - height: 3rem; -} - -// Search box -.search-box { - position: relative; - min-width: 200px; - .search-icon { + .calendar-icon { position: absolute; - right: 0.75rem; + right: 10px; top: 50%; transform: translateY(-50%); - color: #6c757d; pointer-events: none; - } - - .form-control { - padding-right: 2.5rem; - } -} - -// Active filter badge -.active-filter-badge { - animation: fadeIn 0.3s ease; -} - -@keyframes fadeIn { - from { - opacity: 0; - transform: translateY(-10px); - } - to { - opacity: 1; - transform: translateY(0); - } -} - -// Mobile responsive adjustments -@media (max-width: 768px) { - .date-filter-section { - .btn-group { - width: 100%; - - .btn { - flex: 1; - font-size: 0.75rem; - padding: 0.375rem 0.5rem; - } - } - } - - .table-responsive { - overflow-x: auto; - - table { - min-width: 1200px; - } - } - - .search-box { - min-width: 150px; - } -} - -// Dark mode support (if needed) -[data-bs-theme="dark"] { - .table-light { - background-color: #2d333b !important; - color: #adb5bd; - } - - .search-box .search-icon { - color: #adb5bd; + color: #6c757d; } } \ No newline at end of file diff --git a/src/app/transaction-logs/transaction-logs.component.ts b/src/app/transaction-logs/transaction-logs.component.ts index 319a873..665d877 100644 --- a/src/app/transaction-logs/transaction-logs.component.ts +++ b/src/app/transaction-logs/transaction-logs.component.ts @@ -1,340 +1,171 @@ -import { Component, OnInit, OnDestroy } from '@angular/core'; -import { CommonModule } from '@angular/common'; +import { Component, OnInit } from '@angular/core'; +import { CommonModule, DatePipe } from '@angular/common'; import { TranslateModule } from '@ngx-translate/core'; import { ExcelExportService } from '../shared/services/excel-export.service'; -import { TRANSACTION_LOGS_FILE_NAME } from '../utils/app.constants'; +import { toDateAfterFromDateValidator, TRANSACTION_LOGS_FILE_NAME } from '../utils/app.constants'; import { pageSizeOptions } from '../utils/app.constants'; import { NgSelectModule } from '@ng-select/ng-select'; -import { FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { FormBuilder, FormGroup, FormsModule, ReactiveFormsModule, Validators } from '@angular/forms'; import { TableFilterPipe } from '../shared/pipes/table-filter.pipe'; import { TransactionLog } from "../models/user"; import { URIKey } from '../utils/uri-enums'; import { HttpParams } from '@angular/common/http'; import { HttpURIService } from '../app.http.uri.service'; -import { formatDate } from '@angular/common'; -import { Subject } from 'rxjs'; -import { takeUntil, debounceTime } from 'rxjs/operators'; @Component({ selector: 'app-transaction-logs', templateUrl: './transaction-logs.component.html', styleUrls: ['./transaction-logs.component.scss'], - imports: [CommonModule, TranslateModule, NgSelectModule, FormsModule, ReactiveFormsModule, TableFilterPipe] -}) -export class TransactionLogsComponent implements OnInit, OnDestroy { - currentPage: number = 1; - totalCount: number = 0; - renewalDataExpanded: boolean = true; +imports: [ + TranslateModule, + FormsModule, + NgSelectModule, + CommonModule, + ReactiveFormsModule, + TableFilterPipe + ], + providers: [DatePipe]}) +export class TransactionLogsComponent implements OnInit { + logsSearchForm!: FormGroup; + pageSizeOptions = pageSizeOptions; - itemsPerPage: number = 10; - transactionLog: TransactionLog[] = []; + itemsPerPage = 10; + currentPage = 1; + maxDate: string; + searchText = ''; + + logsDataExpanded = true; isLoading = false; - errorMessage: string = ''; - searchText: string = ''; - allItems: TransactionLog[] = []; - transactionDataExpanded: boolean = true; - - // Date range properties - fromDate: string = ''; - toDate: string = ''; - maxDate: string = ''; - showDateFilters: boolean = false; - - // Search subject for debouncing - private searchSubject = new Subject(); - private destroy$ = new Subject(); - + + /** DATA LAYERS (do not mix these) */ + allItems: TransactionLog[] = []; // raw API data + filteredItems: TransactionLog[] = []; // after search + pagedItems: TransactionLog[] = []; // table view + constructor( - private excelExportService: ExcelExportService, + private fb: FormBuilder, private httpService: HttpURIService, - ) {} - - ngOnInit(): void { - // Set max date to today - this.maxDate = formatDate(new Date(), 'yyyy-MM-dd', 'en-US'); - - // Set default date range (last 7 days) - const today = new Date(); - const lastWeek = new Date(); - lastWeek.setDate(today.getDate() - 7); - - this.fromDate = formatDate(lastWeek, 'yyyy-MM-dd', 'en-US'); - this.toDate = formatDate(today, 'yyyy-MM-dd', 'en-US'); - - // Set up search debouncing - this.setupSearchDebounce(); - - this.loadTransactionLogs(); + private datePipe: DatePipe, + private excelExportService: ExcelExportService, + ) { + this.maxDate = new Date().toISOString().split('T')[0]; } - ngOnDestroy(): void { - this.destroy$.next(); - this.destroy$.complete(); - this.searchSubject.complete(); + ngOnInit(): void { + this.logsSearchForm = this.fb.group( + { + fromDate: ['', Validators.required], + toDate: ['', Validators.required], + }, + { validators: toDateAfterFromDateValidator }, + ); } - private setupSearchDebounce(): void { - this.searchSubject.pipe( - takeUntil(this.destroy$), - debounceTime(300) - ).subscribe((searchValue: string) => { - this.searchText = searchValue; - this.currentPage = 1; - this.loadTransactionLogs(); - }); - } + getlogsData(): void { + if (this.logsSearchForm.invalid) return; - loadTransactionLogs(): void { this.isLoading = true; - this.errorMessage = ''; - - // Validate date range before making API call - if (!this.validateDateRange()) { - this.isLoading = false; - return; - } - - // Build query parameters - let params = new HttpParams(); - - // Add pagination parameters - params = params.set('page', this.currentPage.toString()); - params = params.set('limit', this.itemsPerPage.toString()); - - // Add date filters if provided - if (this.fromDate) { - params = params.set('fromDate', this.fromDate); - } - - if (this.toDate) { - params = params.set('toDate', this.toDate); - } - - // Add search filter if provided - if (this.searchText && this.searchText.trim() !== '') { - params = params.set('search', this.searchText.trim()); - } - + + const { fromDate, toDate } = this.logsSearchForm.value; + + const params = new HttpParams() + .set('fromDate', this.datePipe.transform(fromDate, 'dd-MM-yyyy')!) + .set('toDate', this.datePipe.transform(toDate, 'dd-MM-yyyy')!); + this.httpService - .requestGET(URIKey.TRANSACTION_LOGS, params) + .requestGET(URIKey.TRANSACTION_LOGS, params) .subscribe({ - next: (res) => { - const logs = Array.isArray(res) ? res : res?.data; - this.transactionLog = logs ?? []; - this.allItems = [...this.transactionLog]; - - // Get total count from response - if (res?.total !== undefined) { - this.totalCount = res.total; - } else if (res?.pagination?.total !== undefined) { - this.totalCount = res.pagination.total; - } else if (res?.meta?.total !== undefined) { - this.totalCount = res.meta.total; - } else { - this.totalCount = this.transactionLog.length; - } - + next: (response) => { + this.allItems = response ?? []; + this.filteredItems = [...this.allItems]; + this.currentPage = 1; + this.updatePagedItems(); this.isLoading = false; }, - error: (err) => { - console.error('Error fetching transaction logs:', err); - this.errorMessage = err.message || 'Failed to load transaction logs. Please try again.'; - this.transactionLog = []; + error: () => { this.allItems = []; - this.totalCount = 0; + this.filteredItems = []; + this.pagedItems = []; this.isLoading = false; }, - complete: () => { - this.isLoading = false; - } }); } - private validateDateRange(): boolean { - if (this.fromDate && this.toDate) { - const from = new Date(this.fromDate); - const to = new Date(this.toDate); - - if (from > to) { - this.errorMessage = 'From date cannot be after To date'; - return false; - } - - // Optional: Validate date range is not too wide - const diffTime = Math.abs(to.getTime() - from.getTime()); - const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24)); - - if (diffDays > 365) { - this.errorMessage = 'Date range cannot exceed 1 year'; - return false; - } + /** SEARCH — filters DATA, not DOM */ + applySearch(): void { + const value = this.searchText.toLowerCase().trim(); + + if (!value) { + this.filteredItems = [...this.allItems]; + } else { + this.filteredItems = this.allItems.filter((item) => + [ + 'logId', + 'porOrgacode', + 'transactionID', + 'drMbmbkmsnumber', + 'crMbmbkmsnumber', + 'crPcaglacode', + 'drPcaGlacode', + 'amount', + 'paymentMode', + 'ppmPymdcode', + 'sgtGntrdate', + 'channelCode', + 'createdAt', + 'transactionUri', + 'transactionCode', + ].some((key) => + String((item as any)[key] ?? '') + .toLowerCase() + .includes(value), + ), + ); } - - return true; - } - // Date filter change handler - onDateFilterChange(): void { this.currentPage = 1; - this.loadTransactionLogs(); + this.updatePagedItems(); } - // Clear date filters - clearDateFilters(): void { - this.fromDate = ''; - this.toDate = ''; - this.currentPage = 1; - this.loadTransactionLogs(); - } - - // Toggle date filter visibility - toggleDateFilters(): void { - this.showDateFilters = !this.showDateFilters; - } - - // Apply default date range (Last 7 days) - applyLast7Days(): void { - const today = new Date(); - const lastWeek = new Date(); - lastWeek.setDate(today.getDate() - 7); - - this.fromDate = formatDate(lastWeek, 'yyyy-MM-dd', 'en-US'); - this.toDate = formatDate(today, 'yyyy-MM-dd', 'en-US'); - this.onDateFilterChange(); - } - - // Apply default date range (Last 30 days) - applyLast30Days(): void { - const today = new Date(); - const lastMonth = new Date(); - lastMonth.setDate(today.getDate() - 30); - - this.fromDate = formatDate(lastMonth, 'yyyy-MM-dd', 'en-US'); - this.toDate = formatDate(today, 'yyyy-MM-dd', 'en-US'); - this.onDateFilterChange(); - } - - // Apply default date range (This month) - applyThisMonth(): void { - const today = new Date(); - const firstDay = new Date(today.getFullYear(), today.getMonth(), 1); - - this.fromDate = formatDate(firstDay, 'yyyy-MM-dd', 'en-US'); - this.toDate = formatDate(today, 'yyyy-MM-dd', 'en-US'); - this.onDateFilterChange(); - } - - // Apply date range (Last 3 months) - applyLast3Months(): void { - const today = new Date(); - const threeMonthsAgo = new Date(); - threeMonthsAgo.setMonth(today.getMonth() - 3); - - this.fromDate = formatDate(threeMonthsAgo, 'yyyy-MM-dd', 'en-US'); - this.toDate = formatDate(today, 'yyyy-MM-dd', 'en-US'); - this.onDateFilterChange(); - } - - toggleTableCard(): void { - this.transactionDataExpanded = !this.transactionDataExpanded; - } - - itemsPerPageChanged(): void { - this.currentPage = 1; - this.loadTransactionLogs(); - } - - onSearch(value: string): void { - this.searchSubject.next(value); + updatePagedItems(): void { + const start = (this.currentPage - 1) * this.itemsPerPage; + const end = start + this.itemsPerPage; + this.pagedItems = this.filteredItems.slice(start, end); } totalPages(): number { - if (this.totalCount === 0) return 1; - return Math.ceil(this.totalCount / this.itemsPerPage); -} + return Math.ceil(this.filteredItems.length / this.itemsPerPage); + } previousPage(): void { if (this.currentPage > 1) { this.currentPage--; - this.loadTransactionLogs(); + this.updatePagedItems(); } } nextPage(): void { if (this.currentPage < this.totalPages()) { this.currentPage++; - this.loadTransactionLogs(); + this.updatePagedItems(); } } - - exportDataInExcel(): void { - // Build export parameters - let params = new HttpParams(); - - // Include date filters in export if they are set - if (this.fromDate) { - params = params.set('fromDate', this.fromDate); - } - - if (this.toDate) { - params = params.set('toDate', this.toDate); - } - - if (this.searchText && this.searchText.trim() !== '') { - params = params.set('search', this.searchText.trim()); - } - - // For large exports, you might want to fetch all data without pagination - params = params.set('limit', '10000'); // Adjust as needed - - this.httpService - .requestGET(URIKey.TRANSACTION_LOGS, params) - .subscribe({ - next: (res) => { - const logs = Array.isArray(res) ? res : res?.data; - const dataToExport = logs ?? []; - - if (dataToExport.length === 0) { - this.errorMessage = 'No data to export'; - return; - } - - // Generate filename with date range if applicable - let fileName = TRANSACTION_LOGS_FILE_NAME; - if (this.fromDate || this.toDate) { - const from = this.fromDate ? formatDate(new Date(this.fromDate), 'dd-MMM-yyyy', 'en-US') : 'All'; - const to = this.toDate ? formatDate(new Date(this.toDate), 'dd-MMM-yyyy', 'en-US') : 'All'; - fileName = `TransactionLogs_${from}_to_${to}`; - } - - this.excelExportService.exportExcel(dataToExport, fileName); - }, - error: (err) => { - console.error('Error exporting data:', err); - this.errorMessage = 'Failed to export data. Please try again.'; - } - }); - } - // Get filtered items for display (used in template) - get filteredItems(): TransactionLog[] { - return this.allItems; + itemsPerPageChanged(): void { + this.currentPage = 1; + this.updatePagedItems(); } - // Format date for display - formatDateDisplay(dateString: string): string { - if (!dateString) return ''; - return formatDate(new Date(dateString), 'MMM dd, yyyy', 'en-US'); + toggleTableCard(): void { + this.logsDataExpanded = !this.logsDataExpanded; } - // Check if date filter is active - get isDateFilterActive(): boolean { - return !!this.fromDate || !!this.toDate; + exportDataInExcel(): void { + this.excelExportService.exportExcel( + this.allItems, + TRANSACTION_LOGS_FILE_NAME, + ); } - get pagedItems(): TransactionLog[] { - const start = (this.currentPage - 1) * this.itemsPerPage; - return this.allItems.slice(start, start + this.itemsPerPage); -} } \ No newline at end of file diff --git a/src/assets/i18n/English.json b/src/assets/i18n/English.json index 7acb14a..ea317e0 100644 --- a/src/assets/i18n/English.json +++ b/src/assets/i18n/English.json @@ -301,5 +301,6 @@ "expand": "Expand", "dateFilter": "Date Filter", "showDateFilters":"Show Date Filter", - "hideDateFilters":"Hide Date Filter" + "hideDateFilters":"Hide Date Filter", + "transactionLogDetails": "Transaction Logs Details" }