@@ -11,14 +11,15 @@ import { SearchButtonComponent } from '../../component/search-button/search-butt
1111
1212/**
1313 * ListExpensesComponent
14- *
14+ *
1515 * Displays a list of expenses with support for:
1616 * - Sorting
1717 * - Filtering (date range, categories, payment mode, extra spending)
1818 * - Searching
1919 * - Editing and deleting expenses
2020 * - Viewing expense details in a modal
2121 */
22+
2223@Component ( {
2324 standalone : true ,
2425 selector : 'app-list-expenses' ,
@@ -33,37 +34,34 @@ export class ListExpensesComponent implements OnInit {
3334 /** All categories, sorted by expense count. */
3435 categories : Category [ ] = [ ] ;
3536
37+ /** Currently selected expense (for modal view/edit). */
38+ selectedExpense : Expense | null = null ;
39+
3640 /** User's preferred currency (retrieved from UserService). */
3741 currency : string | null ;
3842
39- /** Controls visibility of sort dropdown. */
40- isSortByDropdownOpen : boolean = false ;
41-
4243 /** Currently selected sort option label. */
4344 selectedFieldName : string = 'Sort By' ;
4445
45- /** Controls visibility of filter dropdown. */
46- isFilterDropdownOpen : boolean = false ;
47-
48- /** Currently selected expense (for modal view/edit). */
49- selectedExpense : Expense | null = null ;
46+ /** Search query for filtering expenses. */
47+ searchQuery : string = '' ;
5048
5149 /** Sort direction (1 = asc, -1 = desc). */
5250 fieldDirection : number = 0 ;
5351
5452 /** Index of column used for sorting. */
5553 fieldColIndex : number = 0 ;
5654
57- /** Whether the expense edit modal is open . */
58- isEditOpen : boolean = false ;
55+ /** Total amount of currently visible expenses . */
56+ totalAmount : number = 0 ;
5957
6058 /** Filter state before being applied. */
6159 filter = {
6260 fromDate : '' ,
6361 toDate : '' ,
6462 selectedCategoryIds : [ ] as string [ ] ,
6563 paymentMode : [ ] as string [ ] ,
66- isExtraSpending : true
64+ isExtraSpending : false
6765 } ;
6866
6967 /** Currently applied filters (saved state). */
@@ -72,17 +70,20 @@ export class ListExpensesComponent implements OnInit {
7270 toDate : '' ,
7371 selectedCategoryIds : [ ] as string [ ] ,
7472 paymentMode : [ ] as string [ ] ,
75- isExtraSpending : true
73+ isExtraSpending : false
7674 } ;
7775
78- /** Whether filter is active. */
79- isFiltered : boolean = false ;
80-
8176 /** Whether sorting is active. */
8277 isSorted : boolean = false ;
8378
84- /** Total amount of currently visible expenses. */
85- totalAmount : number = 0 ;
79+ /** Whether the expense edit modal is open. */
80+ isEditOpen : boolean = false ;
81+
82+ /** Controls visibility of sort dropdown. */
83+ isSortByDropdownOpen : boolean = false ;
84+
85+ /** Controls visibility of filter dropdown. */
86+ isFilterDropdownOpen : boolean = false ;
8687
8788 /** Reference to sort dropdown element. */
8889 @ViewChild ( 'sortRef' ) sortRef ! : ElementRef ;
@@ -129,14 +130,6 @@ export class ListExpensesComponent implements OnInit {
129130 } catch ( err ) {
130131 console . error ( "Failed to load expenses:" , err ) ;
131132 }
132- finally {
133- if ( this . isSorted ) {
134- this . sortList ( this . fieldColIndex , this . selectedFieldName , this . fieldDirection ) ;
135- }
136- if ( this . isFiltered ) {
137- this . applyFilter ( ) ;
138- }
139- }
140133 }
141134
142135 /**
@@ -178,38 +171,13 @@ export class ListExpensesComponent implements OnInit {
178171 this . isSortByDropdownOpen = ! this . isSortByDropdownOpen ;
179172 }
180173
181- /**
182- * Handles document click to close dropdowns when clicking outside.
183- * @param event Mouse click event
184- */
185- @HostListener ( 'document:click' , [ '$event' ] )
186- onDocumentClick ( event : MouseEvent ) : void {
187- const target = event . target as HTMLElement ;
188- if ( this . isSortByDropdownOpen && this . sortRef && ! this . sortRef . nativeElement . contains ( target ) ) {
189- this . toggleSortByDropdown ( ) ;
190- }
191-
192- if ( this . isFilterDropdownOpen && this . filterRef && ! this . filterRef . nativeElement . contains ( target ) ) {
193- this . toggleFilterDropdown ( ) ;
194- }
195- }
196-
197- /** Toggles the filter dropdown and resets filter state accordingly. */
198- toggleFilterDropdown ( ) : void {
199- if ( this . isFiltered ) {
200- this . filter = structuredClone ( this . appliedFilter ) ;
201- } else {
202- this . clearFilter ( ) ;
203- }
204- this . isFilterDropdownOpen = ! this . isFilterDropdownOpen ;
205- }
206-
207174 /**
208175 * Applies the current filter to the expense list.
209176 */
210177 applyFilter ( ) : void {
211178 this . totalAmount = 0 ;
212- let filtered = this . expenses ;
179+ this . searchData ( this . searchQuery ) ;
180+ let filtered : Expense [ ] = this . expenses ;
213181 this . appliedFilter = structuredClone ( this . filter ) ;
214182
215183 if ( this . appliedFilter . fromDate ) {
@@ -241,7 +209,26 @@ export class ListExpensesComponent implements OnInit {
241209 this . sortList ( this . fieldColIndex , this . selectedFieldName , this . fieldDirection ) ;
242210 }
243211 this . isFilterDropdownOpen = false ;
244- this . isFiltered = true ;
212+ }
213+
214+ /** Toggles the filter dropdown and resets filter state accordingly. */
215+ toggleFilterDropdown ( ) : void {
216+ this . filter = structuredClone ( this . appliedFilter ) ;
217+ this . isFilterDropdownOpen = ! this . isFilterDropdownOpen ;
218+ }
219+
220+ /**
221+ * Handles payment mode checkbox changes inside filter modal.
222+ * @param event Checkbox change event
223+ * @param categoryId Payment mode value
224+ */
225+ onPaymentModeCheckboxChange ( event : Event , categoryId : string ) : void {
226+ const checkbox = event . target as HTMLInputElement ;
227+ if ( checkbox . checked ) {
228+ this . filter . paymentMode . push ( categoryId ) ;
229+ } else {
230+ this . filter . paymentMode = this . filter . paymentMode . filter ( id => id !== categoryId ) ;
231+ }
245232 }
246233
247234 /**
@@ -258,6 +245,28 @@ export class ListExpensesComponent implements OnInit {
258245 }
259246 }
260247
248+ /** Clears all filters and reloads expenses. */
249+ clearFilter ( ) : void {
250+ this . filter = {
251+ fromDate : '' ,
252+ toDate : '' ,
253+ selectedCategoryIds : [ ] ,
254+ paymentMode : [ ] ,
255+ isExtraSpending : false
256+ } ;
257+ }
258+
259+ /** Clears the applied filter. */
260+ clearAppliedFilter ( ) : void {
261+ this . appliedFilter = {
262+ fromDate : '' ,
263+ toDate : '' ,
264+ selectedCategoryIds : [ ] ,
265+ paymentMode : [ ] ,
266+ isExtraSpending : false
267+ } ;
268+ }
269+
261270 /**
262271 * Opens the expense modal with selected expense.
263272 * @param expense Expense to display
@@ -283,7 +292,6 @@ export class ListExpensesComponent implements OnInit {
283292 this . expenseService . update ( expense_id , newData ) ;
284293 this . toastService . show ( 'Expense updated successfully' , 'success' ) ;
285294 this . isEditOpen = false ;
286- this . listExpenses ( ) ;
287295 }
288296
289297 /**
@@ -296,39 +304,18 @@ export class ListExpensesComponent implements OnInit {
296304 this . toastService . show ( "Expense deleted successfully" , 'success' ) ;
297305 this . closeModal ( ) ;
298306 }
299- this . listExpenses ( ) ;
300- }
301-
302- /** Clears all filters and reloads expenses. */
303- clearFilter ( ) : void {
304- this . filter . fromDate = '' ;
305- this . filter . toDate = '' ;
306- this . filter . selectedCategoryIds = [ ] ;
307- this . filter . paymentMode = [ ] ;
308- this . filter . isExtraSpending = false ;
309- this . isFiltered = false ;
310- this . listExpenses ( ) ;
311- }
312-
313- /** Clears sorting and reloads expenses. */
314- clearSort ( ) : void {
315- this . isSorted = false ;
316- this . selectedFieldName = "Sort By" ;
317- this . listExpenses ( ) ;
318307 }
319308
320309 /**
321- * Handles payment mode checkbox changes inside filter modal.
322- * @param event Checkbox change event
323- * @param categoryId Payment mode value
310+ * Searches expenses by location or note.
311+ * @param query Search query string
324312 */
325- onPaymentModeCheckboxChange ( event : Event , categoryId : string ) : void {
326- const checkbox = event . target as HTMLInputElement ;
327- if ( checkbox . checked ) {
328- this . filter . paymentMode . push ( categoryId ) ;
329- } else {
330- this . filter . paymentMode = this . filter . paymentMode . filter ( id => id !== categoryId ) ;
331- }
313+ searchData ( query : string ) : void {
314+ this . expenses = this . expenseService . getAll ( ) ;
315+ this . expenses = this . expenses . filter ( ex => {
316+ return ( ( ex . location && ex . location . toLowerCase ( ) . includes ( query . toLowerCase ( ) ) ) ||
317+ ( ex . note && ex . note . toLowerCase ( ) . includes ( query . toLowerCase ( ) ) ) ) ;
318+ } ) ;
332319 }
333320
334321 /**
@@ -337,12 +324,8 @@ export class ListExpensesComponent implements OnInit {
337324 */
338325 onSearch ( query : string ) : void {
339326 this . totalAmount = 0 ;
340- console . log ( 'Parent received search query:' , query ) ;
341- this . expenses = this . expenseService . getAll ( ) ;
342- this . expenses = this . expenses . filter ( ex => {
343- return ( ( ex . location && ex . location . toLowerCase ( ) . includes ( query . toLowerCase ( ) ) ) ||
344- ( ex . note && ex . note . toLowerCase ( ) . includes ( query . toLowerCase ( ) ) ) ) ;
345- } ) ;
327+ this . searchQuery = query ;
328+ this . searchData ( this . searchQuery ) ;
346329 this . expenses . forEach ( ( val ) => {
347330 this . totalAmount = this . totalAmount + val . amount ;
348331 } )
@@ -352,13 +335,25 @@ export class ListExpensesComponent implements OnInit {
352335
353336 /** Resets both filters and sorting without reloading expenses. */
354337 clearFiltersAndSorting ( ) : void {
355- this . filter . fromDate = '' ;
356- this . filter . toDate = '' ;
357- this . filter . selectedCategoryIds = [ ] ;
358- this . filter . paymentMode = [ ] ;
359- this . filter . isExtraSpending = false ;
360- this . isFiltered = false ;
338+ this . clearFilter ( ) ;
339+ this . clearAppliedFilter ( ) ;
361340 this . isSorted = false ;
362341 this . selectedFieldName = "Sort By" ;
363342 }
364- }
343+
344+ /**
345+ * Handles document click to close dropdowns when clicking outside.
346+ * @param event Mouse click event
347+ */
348+ @HostListener ( 'document:click' , [ '$event' ] )
349+ onDocumentClick ( event : MouseEvent ) : void {
350+ const target = event . target as HTMLElement ;
351+ if ( this . isSortByDropdownOpen && this . sortRef && ! this . sortRef . nativeElement . contains ( target ) ) {
352+ this . toggleSortByDropdown ( ) ;
353+ }
354+
355+ if ( this . isFilterDropdownOpen && this . filterRef && ! this . filterRef . nativeElement . contains ( target ) ) {
356+ this . toggleFilterDropdown ( ) ;
357+ }
358+ }
359+ }
0 commit comments