Skip to content

Commit ce7c697

Browse files
committed
update
1 parent 37ed2de commit ce7c697

5 files changed

Lines changed: 120 additions & 101 deletions

File tree

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

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,32 @@ export class ExpenseListComponent {
2626

2727
getFormattedDate(exp: any): string {
2828
const date = new Date(exp.date);
29-
return date.toLocaleDateString();
29+
30+
const days = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
31+
const months = [
32+
"Jan", "Feb", "Mar", "Apr", "May", "Jun",
33+
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
34+
];
35+
36+
const dayName = days[date.getDay()];
37+
const day = date.getDate();
38+
const month = months[date.getMonth()];
39+
const year = date.getFullYear();
40+
41+
const suffix = (d: number): string => {
42+
if (d > 3 && d < 21) return "th";
43+
switch (d % 10) {
44+
case 1: return "st";
45+
case 2: return "nd";
46+
case 3: return "rd";
47+
default: return "th";
48+
}
49+
};
50+
51+
return `${dayName} ${day}${suffix(day)} ${month} ${year}`;
3052
}
3153

54+
3255
darkenColor(color: string, percent: number): string {
3356
try {
3457
const num = parseInt(color.replace('#', ''), 16);

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

Lines changed: 92 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -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+
}

src/app/service/gemini-api/gemini-api.service.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ You are a polite and helpful financial assistant AI. Your sole purpose is to hel
7575
- Structure your response using bullet points, short sentences, or paragraph style that's visually appealing.
7676
- Do not add any extra message at the beginning.
7777
78-
🚫 When the user asks something unrelated (e.g., weather, politics, personal advice), respond with:
78+
🚫 When the user asks something unrelated (e.g., weather, politics), respond with:
7979
- "❌ I'm here only to help with your expense data. Please ask something related to your recent spending."
8080
- "⚠️ I cannot process questions outside your expense data."
8181
- "🛑 Let’s keep this focused on your expenses so I can assist you better."

src/app/service/saavan-api/saavan.service.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ ${formatField('Name', currentSong.name)}${formatField('Type', currentSong.type)}
6363
- Suggest the next song that is the most accurate match in mood, vibe, and style to provide a smooth and positive user experience.
6464
- Use only the current song’s metadata to infer the best next song.
6565
- Do not include any additional text, explanation, or formatting in your response.
66+
- Do not repeat the already suggested song.
6667
6768
Provide only the JSON object and no extra text, no backticks, no markdown formatting:
6869
{

src/environments/environments.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,13 @@ export const environment = {
1313
* API key for accessing the Gemini AI API.
1414
* Replace or secure this key before deploying to production.
1515
*/
16-
geminiApiKey: 'AIzaSyAZT2MYoLB9gRwRa6NRan8NdFSVmmwzz04',
16+
geminiApiKey: 'AIzaSyBkBmHCsVna6d8bPYjQQhbpdymn7_Nvm2w',
1717

1818
/**
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)