Skip to content

Commit 6b45f9a

Browse files
committed
add search button on list page
1 parent 139781a commit 6b45f9a

11 files changed

Lines changed: 139 additions & 6 deletions

File tree

374 Bytes
Loading
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
/* Optional: smoother animation for width transition */
2+
:host {
3+
display: block;
4+
}
5+
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<div class="fixed bottom-16 right-5 z-50 flex items-center">
2+
<!-- Animated container -->
3+
<div
4+
class="flex items-center bg-[var(--color-surface)] shadow-lg rounded-full overflow-hidden transition-all duration-300 ease-in-out"
5+
[ngClass]="isOpen ? 'w-[90vw] md:w-[400px] pl-3 pr-1 h-14' : 'w-14 h-14 justify-center'">
6+
<!-- Search input -->
7+
<input *ngIf="isOpen" #searchInput [(ngModel)]="query" (keyup.enter)="onSearch()" type="text" maxlength="100"
8+
placeholder="Search..." class="flex-1 outline-none text-sm px-2 text-[var(--input-text)] bg-transparent focus:ring-[var(--theme-color)]
9+
transition-all duration-200" />
10+
<!-- Search button -->
11+
<button (click)="toggleSearch()"
12+
class="flex items-center justify-center w-12 h-12 bg-[var(--theme-color)] text-white rounded-full transition">
13+
<!-- Search icon (when closed) -->
14+
<img *ngIf="!isOpen" src="assets/img/icon/icons8-search-50.png" alt="Search" class="w-5 h-5 invert" />
15+
16+
<!-- Close icon (when open) -->
17+
<img *ngIf="isOpen" src="assets/img/icon/icons8-cross-50.png" alt="Close" class="w-6 h-6 invert" />
18+
</button>
19+
</div>
20+
</div>
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { ComponentFixture, TestBed } from '@angular/core/testing';
2+
3+
import { SearchButtonComponent } from './search-button.component';
4+
5+
describe('SearchButtonComponent', () => {
6+
let component: SearchButtonComponent;
7+
let fixture: ComponentFixture<SearchButtonComponent>;
8+
9+
beforeEach(async () => {
10+
await TestBed.configureTestingModule({
11+
imports: [SearchButtonComponent]
12+
})
13+
.compileComponents();
14+
15+
fixture = TestBed.createComponent(SearchButtonComponent);
16+
component = fixture.componentInstance;
17+
fixture.detectChanges();
18+
});
19+
20+
it('should create', () => {
21+
expect(component).toBeTruthy();
22+
});
23+
});
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import { Component, ElementRef, EventEmitter, Output, ViewChild } from '@angular/core';
2+
import { CommonModule } from '@angular/common';
3+
import { FormsModule } from '@angular/forms';
4+
import { GlobalLoaderService } from '../../service/global-loader/global-loader.service';
5+
6+
@Component({
7+
selector: 'app-search-button',
8+
standalone: true,
9+
imports: [CommonModule, FormsModule],
10+
templateUrl: './search-button.component.html',
11+
styleUrls: ['./search-button.component.css']
12+
})
13+
14+
export class SearchButtonComponent {
15+
16+
isOpen = false;
17+
query = '';
18+
19+
constructor(
20+
private globalLoader: GlobalLoaderService
21+
) { }
22+
23+
@ViewChild('searchInput') searchInput!: ElementRef<HTMLInputElement>;
24+
@Output() search = new EventEmitter<string>();
25+
26+
toggleSearch() {
27+
this.isOpen = !this.isOpen;
28+
29+
if (this.isOpen) {
30+
setTimeout(() => {
31+
this.searchInput?.nativeElement.focus();
32+
}, 50);
33+
}
34+
}
35+
36+
onSearch() {
37+
console.log('Child emitting search query:', this.query);
38+
this.search.emit(this.query);
39+
this.searchInput?.nativeElement.blur();
40+
this.globalLoader.show('Searching...');
41+
setTimeout(() => {
42+
this.globalLoader.hide();
43+
}, 500);
44+
}
45+
46+
onInputChange() {
47+
if (this.query.trim().length > 10) {
48+
this.query = this.query.slice(0, 10);
49+
}
50+
}
51+
}

src/app/features/add-expense/add-expense.component.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
<!-- Amount -->
88
<div class="relative w-full md:w-1/2">
99
<label class="block mb-1 font-medium">Amount</label>
10-
<input type="number" min="0" formControlName="amount" class="w-full p-2 rounded border"
10+
<input #amountInput type="number" min="0" formControlName="amount" class="w-full p-2 rounded border"
1111
style="background-color: var(--input-bg); color: var(--input-text); border-color: var(--input-border);"
1212
step="0.01" />
1313

src/app/features/add-expense/add-expense.component.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,9 @@ export class AddExpenseComponent implements OnInit {
4444
/** Controls visibility of note suggestions dropdown */
4545
showNoteSuggestions = false;
4646

47+
/** Reference to the amount input element */
48+
@ViewChild('amountInput') amountInput!: ElementRef<HTMLInputElement>;
49+
4750
/**
4851
* Constructor to inject dependencies
4952
* @param fb FormBuilder instance
@@ -60,6 +63,9 @@ export class AddExpenseComponent implements OnInit {
6063

6164
/** Lifecycle hook that initializes the component */
6265
ngOnInit(): void {
66+
setTimeout(() => {
67+
this.amountInput?.nativeElement.focus();
68+
}, 50);
6369
this.resetFormWithCurrentDateTime();
6470
this.loadSuggestionsFromLocalStorage();
6571
this.onInputChanges();

src/app/features/list-expenses/list-expenses.component.html

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,4 +194,6 @@ <h3 class="text-base font-semibold pb-2 border-b-2 border-[var(--color-gray-500)
194194
</div>
195195
</ng-template>
196196

197+
<app-search-button (search)="onSearch($event)"></app-search-button>
198+
197199
</section>

src/app/features/list-expenses/list-expenses.component.ts

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,12 @@ import { UserService } from '../../service/localStorage/user.service';
77
import { FormsModule } from '@angular/forms';
88
import { ExpenseDetailsModalComponent } from "../../component/list-expenses/expense-details-modal/expense-details-modal.component";
99
import { ExpenseListComponent } from "../../component/list-expenses/expense-list/expense-list.component";
10+
import { SearchButtonComponent } from '../../component/search-button/search-button.component';
1011

1112
@Component({
1213
standalone: true,
1314
selector: 'app-list-expenses',
14-
imports: [CommonModule, FormsModule, ExpenseDetailsModalComponent, ExpenseListComponent],
15+
imports: [CommonModule, FormsModule, ExpenseDetailsModalComponent, ExpenseListComponent, SearchButtonComponent],
1516
templateUrl: './list-expenses.component.html',
1617
styleUrls: ['./list-expenses.component.css'],
1718
})
@@ -158,7 +159,7 @@ export class ListExpensesComponent implements OnInit {
158159

159160
applyFilter() {
160161
this.totalAmount = 0;
161-
let filtered = this.expenseService.getAll();
162+
let filtered = this.expenses;
162163
this.appliedFilter = structuredClone(this.filter);
163164

164165
if (this.appliedFilter.fromDate) {
@@ -172,7 +173,7 @@ export class ListExpensesComponent implements OnInit {
172173
if (this.appliedFilter.selectedCategoryIds.length) {
173174
filtered = filtered.filter(e => this.appliedFilter.selectedCategoryIds.includes(e.category_id));
174175
}
175-
176+
176177
if (this.appliedFilter.paymentMode.length) {
177178
filtered = filtered.filter(e => this.appliedFilter.paymentMode.includes(e.payment_mode));
178179
}
@@ -254,4 +255,29 @@ export class ListExpensesComponent implements OnInit {
254255
this.filter.paymentMode = this.filter.paymentMode.filter(id => id !== categoryId);
255256
}
256257
}
258+
259+
onSearch(query: string) {
260+
this.totalAmount = 0;
261+
console.log('Parent received search query:', query);
262+
this.expenses = this.expenseService.getAll();
263+
this.expenses = this.expenses.filter(ex => {
264+
return ((ex.location && ex.location.toLowerCase().includes(query.toLowerCase())) || (ex.note && ex.note.toLowerCase().includes(query.toLowerCase())));
265+
});
266+
this.expenses.forEach((val) => {
267+
this.totalAmount = this.totalAmount + val.amount;
268+
})
269+
this.totalAmount = parseFloat(this.totalAmount.toFixed(2));
270+
this.clearFiltersAndSorting();
271+
}
272+
273+
clearFiltersAndSorting() {
274+
this.filter.fromDate = '';
275+
this.filter.toDate = '';
276+
this.filter.selectedCategoryIds = [];
277+
this.filter.paymentMode = [];
278+
this.filter.isExtraSpending = false;
279+
this.isFiltered = false;
280+
this.isSorted = false;
281+
this.selectedFieldName = "Sort By";
282+
}
257283
}

src/environments/environments.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ export const environment = {
1919
* Current environment type.
2020
* Possible values: 'local', 'live'
2121
*/
22-
developmentEnvironment: 'live', // Change to 'live' for production or 'local' for local development
22+
developmentEnvironment: 'local', // Change to 'live' for production or 'local' for local development
2323

2424
/**
2525
* Application version.

0 commit comments

Comments
 (0)