Skip to content

Commit ac966cf

Browse files
committed
2 parents 9dbe81c + d44d676 commit ac966cf

62 files changed

Lines changed: 2785 additions & 350 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ This application leverages **Angular** as the frontend framework and **Tailwind
1717
| Angular | Frontend application logic |
1818
| Tailwind CSS | Styling and responsive UI |
1919
| LocalStorage | Persistent data storage |
20+
| Django | Backend api logic |
2021

2122
---
2223

@@ -60,6 +61,9 @@ All expenses are stored in the device's **LocalStorage** and reflected immediate
6061
* **Green (Emerald)** – default < ₹500
6162
* A **summary table** displays each color, the number of days, the total expense, and an **Edit button** for updating the thresholds.
6263
* Editing allows users to set a new amount for the corresponding color, immediately updating the heatmap visualization.
64+
* Additionally, users can enable a radio option to automatically set threshold values based on their budget, where
65+
* Rose represents the average spent per day amount.
66+
* Emerald represents the suggested spending per day amount.
6367

6468
---
6569

app-debug.apk

15.7 MB
Binary file not shown.

capacitor.config.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import { CapacitorConfig } from '@capacitor/cli';
2+
3+
/**
4+
* The Capacitor configuration object for the ExpenseWise app.
5+
*/
6+
const config: CapacitorConfig = {
7+
/** Unique app package identifier (reverse-domain format) */
8+
appId: 'com.expensewise.app',
9+
10+
/** Display name of the app as it appears on the device home screen */
11+
appName: 'Exwise',
12+
13+
/** Path to the directory containing the built web application */
14+
webDir: 'dist/expense-wise/browser',
15+
16+
/** Server configuration for live reload and production connections */
17+
server: {
18+
/** Deployed URL of the web app (used for live preview or production API access) */
19+
url: 'https://exwisedev.vercel.app',
20+
21+
/** Allow non-HTTPS traffic for local debugging purposes */
22+
cleartext: true
23+
}
24+
};
25+
26+
export default config;

documentation/components/AddExpenseComponent.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1610,7 +1610,7 @@ <h3 id="inputs">
16101610
<script src="../js/libs/deep-iterator.js"></script>
16111611
<script>
16121612
var COMPONENT_TEMPLATE = '<div><section id="add" class="section p-4 md:p-4"> <!-- <h1 class="text-2xl font-bold mb-6 text-[var(--color-accent)]">➕ Add Expense</h1> --> <form [formGroup]="expenseForm" (ngSubmit)="onSubmit()" class="space-y-4 text-[var(--color-text)]"> <!-- Amount and Category --> <div class="flex flex-col md:flex-row md:space-x-4"> <!-- Amount --> <div class="relative w-full md:w-1/2"> <label class="block mb-1 font-medium">Amount</label> <input #amountInput type="number" min="0" max="100000000" formControlName="amount" class="w-full p-2 rounded border" style="background-color: var(--input-bg); color: var(--input-text); border-color: var(--input-border);" step="0.01" /> <div class="error-message text-[var(--color-error)]" *ngIf="expenseForm.get(\'amount\')?.touched && expenseForm.get(\'amount\')?.errors?.[\'min\']"> Amount cannot be negative </div> <div class="error-message text-[var(--color-error)]" *ngIf="expenseForm.get(\'amount\')?.touched && expenseForm.get(\'amount\')?.errors?.[\'max\']"> Amount cannot exceed 100,000,000 </div> <div class="error-message text-[var(--color-error)]" *ngIf="expenseForm.get(\'amount\')?.touched && expenseForm.get(\'amount\')?.errors?.[\'required\']"> Amount is required </div> </div> <!-- Category --> <div class="w-full md:w-1/2 mt-4 md:mt-0"> <label class="block mb-1 font-medium">Category</label> <app-category-dropdown [dropdownMaxHeightClass]="\'max-h-60\'" [selectedCategoryName]="selectedCategoryName" (categorySelected)="onCategorySelected($event)"></app-category-dropdown> <div class="error-message text-[var(--color-error)]" *ngIf="expenseForm.get(\'category_id\')?.touched && expenseForm.get(\'category_id\')?.invalid"> Category is required </div> </div> </div> <!-- Date and Time --> <div class="flex flex-row space-x-2"> <div class="w-full md:w-1/2 flex flex-col"> <label class="block mb-1 font-medium">Date</label> <input type="date" formControlName="date" class="w-full p-2 rounded border bg-[var(--input-bg)] text-[var(--input-text)] border-[var(--input-border)]" /> <div *ngIf="expenseForm.get(\'date\')?.touched && expenseForm.get(\'date\')?.invalid" class="text-[var(--color-error)] text-sm mt-1"> Date is required </div> </div> <div class="w-full md:w-1/2 flex flex-col"> <label class="block mb-1 font-medium">Time</label> <input type="time" formControlName="time" step="1" class="w-full p-2 rounded border bg-[var(--input-bg)] text-[var(--input-text)] border-[var(--input-border)]" /> <div *ngIf="expenseForm.get(\'time\')?.touched && expenseForm.get(\'time\')?.invalid" class="text-[var(--color-error)] text-sm mt-1"> Time is required </div> </div> </div> <!-- Payment Mode and Location --> <div class="flex flex-col md:flex-row md:space-x-4"> <div class="w-full md:w-1/2"> <label class="block mb-1 font-medium">Payment Mode</label> <select formControlName="payment_mode" class="w-full p-2 rounded border" style="background-color: var(--input-bg); color: var(--input-text); border-color: var(--input-border);"> <option value="UPI">UPI</option> <option value="Cash">Cash</option> <option value="Others">Others</option> </select> </div> <div class="relative w-full md:w-1/2 mt-4 md:mt-0"> <label class="block mb-1 font-medium">Location</label> <input type="text" formControlName="location" maxlength="51" class="w-full p-2 rounded border" style="background-color: var(--input-bg); color: var(--input-text); border-color: var(--input-border);" /> <!-- Location Suggestions --> <ul *ngIf="showLocationSuggestions && filteredLocationSuggestions.length" class="absolute max-h-40 z-50 w-full overflow-y-auto bg-[var(--color-bg)] rounded border" style="background-color: var(--input-bg); color: var(--input-text); border-color: var(--input-border);"> <li *ngFor="let loc of filteredLocationSuggestions" (click)="selectLocationSuggestion(loc)" class="p-2 cursor-pointer hover:bg-[--list-hover]"> {{ loc }} </li> </ul> <div class="error-message text-[var(--color-error)]" *ngIf="expenseForm.get(\'location\')?.touched && expenseForm.get(\'location\')?.errors?.[\'maxlength\']"> Location cannot be more than 50 characters </div> </div> </div> <!-- Note --> <div> <label class="block mb-1 font-medium">Note</label> <textarea formControlName="note" maxlength="101" rows="2" class="w-full p-2 rounded border resize-none" style="background-color: var(--input-bg); color: var(--input-text); border-color: var(--input-border);"></textarea> <!-- Note Suggestions --> <ul *ngIf="showNoteSuggestions && filteredNoteSuggestions.length" class="max-h-40 z-50 w-full overflow-y-auto bg-[var(--color-bg)] rounded border" style="background-color: var(--input-bg); color: var(--input-text); border-color: var(--input-border);"> <li *ngFor="let note of filteredNoteSuggestions" (click)="selectNoteSuggestion(note)" class="p-2 cursor-pointer hover:bg-[--list-hover]"> {{ note }} </li> </ul> <div class="error-message text-[var(--color-error)]" *ngIf="expenseForm.get(\'note\')?.touched && expenseForm.get(\'note\')?.errors?.[\'maxlength\']"> Note cannot be more than 100 characters </div> </div> <!-- Extra Spending Toggle --> <div style="margin-top: 8px;"> <div class="w-full md:w-1/2 flex items-center justify-between md:justify-start md:space-x-4"> <label for="isExtraSpending" class="block font-medium">Mark as Extra Spending</label> <label class="relative inline-flex items-center cursor-pointer"> <input type="checkbox" id="isExtraSpending" formControlName="isExtraSpending" class="sr-only peer"> <div class="w-11 h-6 bg-gray-600 rounded-full peer peer-checked:bg-[var(--color-accent)] transition-all"> </div> <span class="absolute left-1 top-1 w-4 h-4 bg-white rounded-full transition-all peer-checked:translate-x-5"></span> </label> </div> </div> <div> <button type="submit" class="bg-[var(--color-accent)] text-[var(--color-white)] px-4 py-2 rounded w-full"> Add Expense </button> </div> </form></section></div>'
1613-
var COMPONENTS = [{'name': 'AddExpenseComponent', 'selector': 'app-add-expense'},{'name': 'AiComponent', 'selector': 'app-ai'},{'name': 'AppComponent', 'selector': 'app-root'},{'name': 'BudgetComponent', 'selector': 'app-budget'},{'name': 'CalendarComponent', 'selector': 'app-calendar'},{'name': 'CategoryDropdownComponent', 'selector': 'app-category-dropdown'},{'name': 'DownloadComponentComponent', 'selector': 'app-download-component'},{'name': 'ExpenseDetailsModalComponent', 'selector': 'app-expense-details-modal'},{'name': 'ExpenseListComponent', 'selector': 'app-expense-list'},{'name': 'ExpenseWiseComponent', 'selector': 'app-expense-wise'},{'name': 'FooterComponent', 'selector': 'app-footer'},{'name': 'FormModelComponent', 'selector': 'app-form-model'},{'name': 'GlobalLoaderComponent', 'selector': 'app-global-loader'},{'name': 'GraphsComponent', 'selector': 'app-graphs'},{'name': 'HamburgerMenuComponent', 'selector': 'app-hamburger-menu'},{'name': 'HomeComponent', 'selector': 'app-home'},{'name': 'InstallAppPopupComponentComponent', 'selector': 'app-install-app-popup-component'},{'name': 'ListExpensesComponent', 'selector': 'app-list-expenses'},{'name': 'MusicComponent', 'selector': 'app-music'},{'name': 'NavbarComponent', 'selector': 'app-navbar'},{'name': 'PieChartComponent', 'selector': 'app-pie-chart'},{'name': 'PlaylistMusicComponent', 'selector': 'app-playlist-music'},{'name': 'SearchButtonComponent', 'selector': 'app-search-button'},{'name': 'SearchMusicComponent', 'selector': 'app-search-music'},{'name': 'SettingItemComponent', 'selector': 'app-setting-item'},{'name': 'SettingsComponent', 'selector': 'app-settings'},{'name': 'SidebarComponent', 'selector': 'app-sidebar'},{'name': 'TemplatePlaygroundComponent', 'selector': 'template-playground-root'},{'name': 'ToastComponent', 'selector': 'app-toast'}];
1613+
var COMPONENTS = [{'name': 'AddExpenseComponent', 'selector': 'app-add-expense'},{'name': 'AiComponent', 'selector': 'app-ai'},{'name': 'AppComponent', 'selector': 'app-root'},{'name': 'BudgetComponent', 'selector': 'app-budget'},{'name': 'CalendarComponent', 'selector': 'app-calendar'},{'name': 'CategoryDropdownComponent', 'selector': 'app-category-dropdown'},{'name': 'DownloadComponentComponent', 'selector': 'app-download-component'},{'name': 'ExpenseDetailsModalComponent', 'selector': 'app-expense-details-modal'},{'name': 'ExpenseListComponent', 'selector': 'app-expense-list'},{'name': 'ExpenseWiseComponent', 'selector': 'app-expense-wise'},{'name': 'FooterComponent', 'selector': 'app-footer'},{'name': 'FormModelComponent', 'selector': 'app-form-model'},{'name': 'GlobalLoaderComponent', 'selector': 'app-global-loader'},{'name': 'GraphsComponent', 'selector': 'app-graphs'},{'name': 'HamburgerMenuComponent', 'selector': 'app-hamburger-menu'},{'name': 'HomeComponent', 'selector': 'app-home'},{'name': 'InstallAppPopupComponentComponent', 'selector': 'app-install-app-popup-component'},{'name': 'ListExpensesComponent', 'selector': 'app-list-expenses'},{'name': 'MusicComponent', 'selector': 'app-music'},{'name': 'NavbarComponent', 'selector': 'app-navbar'},{'name': 'PieChartComponent', 'selector': 'app-pie-chart'},{'name': 'PlaylistMusicComponent', 'selector': 'app-playlist-music'},{'name': 'SearchButtonComponent', 'selector': 'app-search-button'},{'name': 'SearchMusicComponent', 'selector': 'app-search-music'},{'name': 'SettingItemComponent', 'selector': 'app-setting-item'},{'name': 'SettingsComponent', 'selector': 'app-settings'},{'name': 'SidebarComponent', 'selector': 'app-sidebar'},{'name': 'SplashScreenComponent', 'selector': 'app-splash-screen'},{'name': 'TemplatePlaygroundComponent', 'selector': 'template-playground-root'},{'name': 'ToastComponent', 'selector': 'app-toast'}];
16141614
var DIRECTIVES = [];
16151615
var ACTUAL_COMPONENT = {'name': 'AddExpenseComponent'};
16161616
</script>

documentation/components/AiComponent.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -693,7 +693,7 @@ <h3 id="inputs">
693693
<script src="../js/libs/deep-iterator.js"></script>
694694
<script>
695695
var COMPONENT_TEMPLATE = '<div><section class="section p-4 mb-4"> <div class="transition-all duration-300 max-w-2xl mx-auto"> <div class="sticky top-0 z-30 bg-[var(--color-bg)] py-2 pb-4"> <h2 class="text-xl font-semibold mb-4 text-[var(--theme-color)] text-center">💬 AI Assistant </h2> <!-- Chat-style Input Area --> <div class="flex gap-2 items-center"> <!-- Send Button with Icon --> <!-- Input Field --> <input type="text" [(ngModel)]="userInput" placeholder="Ask something related to your expenses..." class="flex-1 px-4 py-2 rounded-lg border text-[var(--input-text)] bg-[var(--input-bg)] border-[var(--input-border)] focus:outline-none focus:ring-2 focus:ring-[var(--theme-color)] transition-all duration-200" /> <button (click)="send()" class="shrink-0 bg-[var(--theme-color)] p-3 rounded-full shadow hover:scale-105 transition-transform duration-200" title="Send"> <img src="assets/img/icon/icons8-send-48.png" alt="Send" class="w-5 h-5" style="filter:invert(100%)" /> </button> </div> </div> <!-- AI Response Box --> <div *ngIf="responseHtml" class="mt-1 p-1 pb-4 bg-[var(--color-bg)] animate-fade-in overflow-auto" [innerHTML]="responseHtml"></div> </div></section><!-- for chat --><!-- <section class="p-4 section"> <div class="p-4 max-w-xl mx-auto"> <div class="space-y-4 mb-4"> <div *ngFor="let msg of messages"> <div [ngClass]="{ \'text-right\': msg.role === \'user\', \'text-left\': msg.role === \'model\' }"> <div [ngClass]="{ \'bg-blue-100 text-blue-800\': msg.role === \'user\', \'bg-gray-100 text-gray-800\': msg.role === \'model\' }" class="inline-block px-4 py-2 rounded-lg max-w-full overflow-x-auto"> <ng-container *ngIf="msg.role === \'user\'; else markdownReply"> {{ msg.parts[0].text }} </ng-container> <ng-template #markdownReply> <div [innerHTML]="msg.safeHtml"></div> </ng-template> </div> </div> </div> <div *ngIf="loading" class="text-center text-gray-400">Thinking...</div> </div> <div class="flex gap-2"> <input type="text" [(ngModel)]="userInput" placeholder="Type your message..." class="w-full p-2 border rounded" (keydown.enter)="send()" /> <button (click)="send()" class="bg-blue-500 text-white px-4 py-2 rounded" [disabled]="loading"> Send </button> </div> </div></section> --></div>'
696-
var COMPONENTS = [{'name': 'AddExpenseComponent', 'selector': 'app-add-expense'},{'name': 'AiComponent', 'selector': 'app-ai'},{'name': 'AppComponent', 'selector': 'app-root'},{'name': 'BudgetComponent', 'selector': 'app-budget'},{'name': 'CalendarComponent', 'selector': 'app-calendar'},{'name': 'CategoryDropdownComponent', 'selector': 'app-category-dropdown'},{'name': 'DownloadComponentComponent', 'selector': 'app-download-component'},{'name': 'ExpenseDetailsModalComponent', 'selector': 'app-expense-details-modal'},{'name': 'ExpenseListComponent', 'selector': 'app-expense-list'},{'name': 'ExpenseWiseComponent', 'selector': 'app-expense-wise'},{'name': 'FooterComponent', 'selector': 'app-footer'},{'name': 'FormModelComponent', 'selector': 'app-form-model'},{'name': 'GlobalLoaderComponent', 'selector': 'app-global-loader'},{'name': 'GraphsComponent', 'selector': 'app-graphs'},{'name': 'HamburgerMenuComponent', 'selector': 'app-hamburger-menu'},{'name': 'HomeComponent', 'selector': 'app-home'},{'name': 'InstallAppPopupComponentComponent', 'selector': 'app-install-app-popup-component'},{'name': 'ListExpensesComponent', 'selector': 'app-list-expenses'},{'name': 'MusicComponent', 'selector': 'app-music'},{'name': 'NavbarComponent', 'selector': 'app-navbar'},{'name': 'PieChartComponent', 'selector': 'app-pie-chart'},{'name': 'PlaylistMusicComponent', 'selector': 'app-playlist-music'},{'name': 'SearchButtonComponent', 'selector': 'app-search-button'},{'name': 'SearchMusicComponent', 'selector': 'app-search-music'},{'name': 'SettingItemComponent', 'selector': 'app-setting-item'},{'name': 'SettingsComponent', 'selector': 'app-settings'},{'name': 'SidebarComponent', 'selector': 'app-sidebar'},{'name': 'TemplatePlaygroundComponent', 'selector': 'template-playground-root'},{'name': 'ToastComponent', 'selector': 'app-toast'}];
696+
var COMPONENTS = [{'name': 'AddExpenseComponent', 'selector': 'app-add-expense'},{'name': 'AiComponent', 'selector': 'app-ai'},{'name': 'AppComponent', 'selector': 'app-root'},{'name': 'BudgetComponent', 'selector': 'app-budget'},{'name': 'CalendarComponent', 'selector': 'app-calendar'},{'name': 'CategoryDropdownComponent', 'selector': 'app-category-dropdown'},{'name': 'DownloadComponentComponent', 'selector': 'app-download-component'},{'name': 'ExpenseDetailsModalComponent', 'selector': 'app-expense-details-modal'},{'name': 'ExpenseListComponent', 'selector': 'app-expense-list'},{'name': 'ExpenseWiseComponent', 'selector': 'app-expense-wise'},{'name': 'FooterComponent', 'selector': 'app-footer'},{'name': 'FormModelComponent', 'selector': 'app-form-model'},{'name': 'GlobalLoaderComponent', 'selector': 'app-global-loader'},{'name': 'GraphsComponent', 'selector': 'app-graphs'},{'name': 'HamburgerMenuComponent', 'selector': 'app-hamburger-menu'},{'name': 'HomeComponent', 'selector': 'app-home'},{'name': 'InstallAppPopupComponentComponent', 'selector': 'app-install-app-popup-component'},{'name': 'ListExpensesComponent', 'selector': 'app-list-expenses'},{'name': 'MusicComponent', 'selector': 'app-music'},{'name': 'NavbarComponent', 'selector': 'app-navbar'},{'name': 'PieChartComponent', 'selector': 'app-pie-chart'},{'name': 'PlaylistMusicComponent', 'selector': 'app-playlist-music'},{'name': 'SearchButtonComponent', 'selector': 'app-search-button'},{'name': 'SearchMusicComponent', 'selector': 'app-search-music'},{'name': 'SettingItemComponent', 'selector': 'app-setting-item'},{'name': 'SettingsComponent', 'selector': 'app-settings'},{'name': 'SidebarComponent', 'selector': 'app-sidebar'},{'name': 'SplashScreenComponent', 'selector': 'app-splash-screen'},{'name': 'TemplatePlaygroundComponent', 'selector': 'template-playground-root'},{'name': 'ToastComponent', 'selector': 'app-toast'}];
697697
var DIRECTIVES = [];
698698
var ACTUAL_COMPONENT = {'name': 'AiComponent'};
699699
</script>

0 commit comments

Comments
 (0)