+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {{ "activeFilter" | translate }}:
- {{ "from" | translate }} {{ fromDate | date }}
- {{ "to" | translate }}
- {{ toDate | date }}
-
-
-
+
+
+
+
+
+
+
-
-
-
-
- Loading...
-
-
{{ "loadingTransactionLogs" | translate }}
-
- {{ "filteringByDate" | translate }}
-
-
+
+
+
+
+
+
+
+
+
+
+
+
{{ "noTransactionLogsFound" | translate }}
+
-
-
0" class="table-responsive">
-
-
-
- | {{ "logID" | translate }} |
+
+
+
+
+
+ | {{ "logID" | translate }} |
{{ "organization" | translate }} |
{{ "transactionID" | translate }} |
{{ "drAccount" | translate }} |
@@ -190,10 +147,11 @@
{{ "date" | translate }} |
{{ "channel" | translate }} |
{{ "createdAt" | translate }} |
-
-
-
-
+
+
+
+ {{ log.channelCode || '-' }}
| {{ 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 339c092..53cfa71 100644
--- a/src/assets/i18n/English.json
+++ b/src/assets/i18n/English.json
@@ -313,4 +313,4 @@
"noDataInRange": "No transactions found in the selected date range",
"dateRangeError": "Please select a valid date range",
"exportAllData": "Export All Data"
-}
\ No newline at end of file
+}