nameadarsh commited on
Commit
bb321e7
·
verified ·
1 Parent(s): 2155f25

Add 2 files

Browse files
Files changed (1) hide show
  1. index.html +1448 -38
index.html CHANGED
@@ -1,41 +1,1451 @@
1
  <!DOCTYPE html>
2
- <html>
3
- <head>
4
- <title>My app</title>
5
- <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
- <meta charset="utf-8">
 
 
 
 
 
 
 
 
7
  <style>
8
- body {
9
- display: flex;
10
- justify-content: center;
11
- align-items: center;
12
- overflow: hidden;
13
- height: 100dvh;
14
- font-family: "Arial", sans-serif;
15
- text-align: center;
16
- }
17
- .arrow {
18
- position: absolute;
19
- bottom: 32px;
20
- left: 0px;
21
- width: 100px;
22
- transform: rotate(30deg);
23
- }
24
- h1 {
25
- font-size: 50px;
26
- }
27
- h1 span {
28
- color: #acacac;
29
- font-size: 32px;
30
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
31
  </style>
32
- </head>
33
- <body>
34
- <h1>
35
- <span>I'm ready to work,</span><br />
36
- Ask me anything.
37
- </h1>
38
- <img src="https://enzostvs-deepsite.hf.space/arrow.svg" class="arrow" />
39
- <script></script>
40
- <p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=nameadarsh/trial-space" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
41
- </html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <meta name="theme-color" content="#4f46e5">
7
+ <meta name="description" content="Offline Finance Manager">
8
+ <meta name="apple-mobile-web-app-capable" content="yes">
9
+ <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
10
+ <title>FinTrack - Offline Finance Manager</title>
11
+ <script src="https://cdn.tailwindcss.com"></script>
12
+ <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
13
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
14
+ <link rel="manifest" href="/manifest.json">
15
  <style>
16
+ @media (display-mode: standalone) {
17
+ header {
18
+ padding-top: env(safe-area-inset-top);
19
+ }
20
+ }
21
+
22
+ .no-scrollbar::-webkit-scrollbar {
23
+ display: none;
24
+ }
25
+
26
+ .no-scrollbar {
27
+ -ms-overflow-style: none;
28
+ scrollbar-width: none;
29
+ }
30
+
31
+ .fade-in {
32
+ animation: fadeIn 0.3s ease-in-out;
33
+ }
34
+
35
+ @keyframes fadeIn {
36
+ from { opacity: 0; transform: translateY(10px); }
37
+ to { opacity: 1; transform: translateY(0); }
38
+ }
39
+
40
+ .progress-ring__circle {
41
+ transition: stroke-dashoffset 0.35s;
42
+ transform: rotate(-90deg);
43
+ transform-origin: 50% 50%;
44
+ }
45
+
46
+ .dark {
47
+ background-color: #1a1a2e;
48
+ color: #e2e8f0;
49
+ }
50
+
51
+ .dark .bg-white {
52
+ background-color: #16213e !important;
53
+ }
54
+
55
+ .dark .text-gray-800 {
56
+ color: #e2e8f0 !important;
57
+ }
58
+
59
+ .dark .border-gray-200 {
60
+ border-color: #2d3748 !important;
61
+ }
62
+
63
+ .dark .bg-indigo-600 {
64
+ background-color: #4338ca !important;
65
+ }
66
+
67
+ .dark .bg-indigo-100 {
68
+ background-color: #312e81 !important;
69
+ }
70
+
71
+ .dark .text-indigo-600 {
72
+ color: #818cf8 !important;
73
+ }
74
  </style>
75
+ </head>
76
+ <body class="bg-gray-50 min-h-screen">
77
+ <div class="max-w-md mx-auto bg-white shadow-sm min-h-screen flex flex-col">
78
+ <!-- Header -->
79
+ <header class="bg-indigo-600 text-white p-4 sticky top-0 z-10">
80
+ <div class="flex justify-between items-center">
81
+ <h1 class="text-xl font-bold">FinTrack</h1>
82
+ <div class="flex items-center space-x-3">
83
+ <button id="themeToggle" class="text-white">
84
+ <i class="fas fa-moon"></i>
85
+ </button>
86
+ <button id="exportBtn" class="text-white">
87
+ <i class="fas fa-file-export"></i>
88
+ </button>
89
+ </div>
90
+ </div>
91
+
92
+ <!-- Summary Cards -->
93
+ <div class="mt-4 grid grid-cols-2 gap-2">
94
+ <div class="bg-indigo-500 rounded-lg p-3">
95
+ <p class="text-xs text-indigo-100">Total Balance</p>
96
+ <p class="font-bold text-lg" id="totalBalance">$0.00</p>
97
+ </div>
98
+ <div class="bg-indigo-500 rounded-lg p-3">
99
+ <p class="text-xs text-indigo-100">This Month</p>
100
+ <p class="font-bold text-lg" id="monthlyExpense">$0.00</p>
101
+ </div>
102
+ </div>
103
+ </header>
104
+
105
+ <!-- Main Content -->
106
+ <main class="flex-1 overflow-y-auto no-scrollbar p-4">
107
+ <!-- Navigation Tabs -->
108
+ <div class="flex border-b border-gray-200 mb-4">
109
+ <button id="dashboardTab" class="flex-1 py-2 font-medium text-indigo-600 border-b-2 border-indigo-600">Dashboard</button>
110
+ <button id="transactionsTab" class="flex-1 py-2 font-medium text-gray-500">Transactions</button>
111
+ <button id="budgetsTab" class="flex-1 py-2 font-medium text-gray-500">Budgets</button>
112
+ <button id="goalsTab" class="flex-1 py-2 font-medium text-gray-500">Goals</button>
113
+ </div>
114
+
115
+ <!-- Dashboard View -->
116
+ <div id="dashboardView" class="space-y-4">
117
+ <div class="bg-white rounded-lg shadow p-4">
118
+ <h2 class="font-bold text-lg mb-2">Expense Breakdown</h2>
119
+ <div class="h-64">
120
+ <canvas id="expenseChart"></canvas>
121
+ </div>
122
+ </div>
123
+
124
+ <div class="bg-white rounded-lg shadow p-4">
125
+ <h2 class="font-bold text-lg mb-2">Recent Transactions</h2>
126
+ <div id="recentTransactions" class="space-y-2">
127
+ <!-- Transactions will be added here -->
128
+ </div>
129
+ <button id="addTransactionBtn" class="mt-3 w-full bg-indigo-600 text-white py-2 rounded-lg flex items-center justify-center space-x-2">
130
+ <i class="fas fa-plus"></i>
131
+ <span>Add Transaction</span>
132
+ </button>
133
+ </div>
134
+
135
+ <div class="bg-white rounded-lg shadow p-4">
136
+ <h2 class="font-bold text-lg mb-2">Savings Goals</h2>
137
+ <div id="goalsList" class="space-y-3">
138
+ <!-- Goals will be added here -->
139
+ </div>
140
+ <button id="addGoalBtn" class="mt-3 w-full bg-indigo-600 text-white py-2 rounded-lg flex items-center justify-center space-x-2">
141
+ <i class="fas fa-plus"></i>
142
+ <span>Add Goal</span>
143
+ </button>
144
+ </div>
145
+ </div>
146
+
147
+ <!-- Transactions View -->
148
+ <div id="transactionsView" class="hidden">
149
+ <div class="flex justify-between items-center mb-4">
150
+ <h2 class="font-bold text-lg">All Transactions</h2>
151
+ <div class="flex space-x-2">
152
+ <button id="filterBtn" class="bg-gray-100 px-3 py-1 rounded-lg text-sm">
153
+ <i class="fas fa-filter"></i>
154
+ </button>
155
+ <button id="sortBtn" class="bg-gray-100 px-3 py-1 rounded-lg text-sm">
156
+ <i class="fas fa-sort-amount-down"></i>
157
+ </button>
158
+ </div>
159
+ </div>
160
+
161
+ <div id="allTransactions" class="space-y-2">
162
+ <!-- All transactions will be added here -->
163
+ </div>
164
+ </div>
165
+
166
+ <!-- Budgets View -->
167
+ <div id="budgetsView" class="hidden">
168
+ <h2 class="font-bold text-lg mb-4">Category Budgets</h2>
169
+
170
+ <div id="budgetsList" class="space-y-3">
171
+ <!-- Budgets will be added here -->
172
+ </div>
173
+
174
+ <button id="addBudgetBtn" class="mt-4 w-full bg-indigo-600 text-white py-2 rounded-lg flex items-center justify-center space-x-2">
175
+ <i class="fas fa-plus"></i>
176
+ <span>Add Budget</span>
177
+ </button>
178
+ </div>
179
+
180
+ <!-- Goals View -->
181
+ <div id="goalsView" class="hidden">
182
+ <h2 class="font-bold text-lg mb-4">Savings Goals</h2>
183
+
184
+ <div id="fullGoalsList" class="space-y-3">
185
+ <!-- Full goals list will be added here -->
186
+ </div>
187
+
188
+ <button id="addGoalBtn2" class="mt-4 w-full bg-indigo-600 text-white py-2 rounded-lg flex items-center justify-center space-x-2">
189
+ <i class="fas fa-plus"></i>
190
+ <span>Add Goal</span>
191
+ </button>
192
+ </div>
193
+ </main>
194
+
195
+ <!-- Bottom Navigation -->
196
+ <nav class="bg-white border-t border-gray-200 p-2 sticky bottom-0">
197
+ <div class="flex justify-around">
198
+ <button class="p-2 text-indigo-600">
199
+ <i class="fas fa-home text-xl"></i>
200
+ </button>
201
+ <button class="p-2 text-gray-500">
202
+ <i class="fas fa-chart-pie text-xl"></i>
203
+ </button>
204
+ <button id="addTransactionBtnBottom" class="bg-indigo-600 text-white p-3 rounded-full -mt-6 shadow-lg">
205
+ <i class="fas fa-plus text-xl"></i>
206
+ </button>
207
+ <button class="p-2 text-gray-500">
208
+ <i class="fas fa-wallet text-xl"></i>
209
+ </button>
210
+ <button class="p-2 text-gray-500">
211
+ <i class="fas fa-cog text-xl"></i>
212
+ </button>
213
+ </div>
214
+ </nav>
215
+ </div>
216
+
217
+ <!-- Add Transaction Modal -->
218
+ <div id="transactionModal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center p-4 z-20 hidden">
219
+ <div class="bg-white rounded-lg w-full max-w-md max-h-[90vh] overflow-y-auto fade-in">
220
+ <div class="p-4 border-b border-gray-200 flex justify-between items-center">
221
+ <h3 class="font-bold text-lg">Add Transaction</h3>
222
+ <button id="closeTransactionModal" class="text-gray-500">
223
+ <i class="fas fa-times"></i>
224
+ </button>
225
+ </div>
226
+
227
+ <div class="p-4 space-y-4">
228
+ <div>
229
+ <label class="block text-sm font-medium text-gray-700 mb-1">Amount</label>
230
+ <input type="number" id="transactionAmount" class="w-full p-2 border border-gray-300 rounded-lg" placeholder="0.00">
231
+ </div>
232
+
233
+ <div>
234
+ <label class="block text-sm font-medium text-gray-700 mb-1">Category</label>
235
+ <div class="relative">
236
+ <select id="transactionCategory" class="w-full p-2 border border-gray-300 rounded-lg appearance-none">
237
+ <option value="food">Food</option>
238
+ <option value="transport">Transport</option>
239
+ <option value="bills">Bills</option>
240
+ <option value="shopping">Shopping</option>
241
+ <option value="entertainment">Entertainment</option>
242
+ <option value="other">Other</option>
243
+ </select>
244
+ <div class="absolute inset-y-0 right-0 flex items-center pr-2 pointer-events-none">
245
+ <i class="fas fa-chevron-down text-gray-400"></i>
246
+ </div>
247
+ </div>
248
+ </div>
249
+
250
+ <div>
251
+ <label class="block text-sm font-medium text-gray-700 mb-1">Description</label>
252
+ <input type="text" id="transactionDescription" class="w-full p-2 border border-gray-300 rounded-lg" placeholder="Optional">
253
+ </div>
254
+
255
+ <div>
256
+ <label class="block text-sm font-medium text-gray-700 mb-1">Date</label>
257
+ <input type="date" id="transactionDate" class="w-full p-2 border border-gray-300 rounded-lg">
258
+ </div>
259
+
260
+ <div class="flex space-x-2">
261
+ <button id="expenseBtn" class="flex-1 bg-red-500 text-white py-2 rounded-lg">
262
+ Expense
263
+ </button>
264
+ <button id="incomeBtn" class="flex-1 bg-green-500 text-white py-2 rounded-lg">
265
+ Income
266
+ </button>
267
+ </div>
268
+ </div>
269
+ </div>
270
+ </div>
271
+
272
+ <!-- Add Goal Modal -->
273
+ <div id="goalModal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center p-4 z-20 hidden">
274
+ <div class="bg-white rounded-lg w-full max-w-md max-h-[90vh] overflow-y-auto fade-in">
275
+ <div class="p-4 border-b border-gray-200 flex justify-between items-center">
276
+ <h3 class="font-bold text-lg">Add Savings Goal</h3>
277
+ <button id="closeGoalModal" class="text-gray-500">
278
+ <i class="fas fa-times"></i>
279
+ </button>
280
+ </div>
281
+
282
+ <div class="p-4 space-y-4">
283
+ <div>
284
+ <label class="block text-sm font-medium text-gray-700 mb-1">Goal Name</label>
285
+ <input type="text" id="goalName" class="w-full p-2 border border-gray-300 rounded-lg" placeholder="e.g. Vacation">
286
+ </div>
287
+
288
+ <div>
289
+ <label class="block text-sm font-medium text-gray-700 mb-1">Target Amount</label>
290
+ <input type="number" id="goalTarget" class="w-full p-2 border border-gray-300 rounded-lg" placeholder="0.00">
291
+ </div>
292
+
293
+ <div>
294
+ <label class="block text-sm font-medium text-gray-700 mb-1">Current Amount</label>
295
+ <input type="number" id="goalCurrent" class="w-full p-2 border border-gray-300 rounded-lg" placeholder="0.00">
296
+ </div>
297
+
298
+ <div>
299
+ <label class="block text-sm font-medium text-gray-700 mb-1">Target Date</label>
300
+ <input type="date" id="goalDate" class="w-full p-2 border border-gray-300 rounded-lg">
301
+ </div>
302
+
303
+ <button id="saveGoalBtn" class="w-full bg-indigo-600 text-white py-2 rounded-lg">
304
+ Save Goal
305
+ </button>
306
+ </div>
307
+ </div>
308
+ </div>
309
+
310
+ <!-- Add Budget Modal -->
311
+ <div id="budgetModal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center p-4 z-20 hidden">
312
+ <div class="bg-white rounded-lg w-full max-w-md max-h-[90vh] overflow-y-auto fade-in">
313
+ <div class="p-4 border-b border-gray-200 flex justify-between items-center">
314
+ <h3 class="font-bold text-lg">Add Budget</h3>
315
+ <button id="closeBudgetModal" class="text-gray-500">
316
+ <i class="fas fa-times"></i>
317
+ </button>
318
+ </div>
319
+
320
+ <div class="p-4 space-y-4">
321
+ <div>
322
+ <label class="block text-sm font-medium text-gray-700 mb-1">Category</label>
323
+ <div class="relative">
324
+ <select id="budgetCategory" class="w-full p-2 border border-gray-300 rounded-lg appearance-none">
325
+ <option value="food">Food</option>
326
+ <option value="transport">Transport</option>
327
+ <option value="bills">Bills</option>
328
+ <option value="shopping">Shopping</option>
329
+ <option value="entertainment">Entertainment</option>
330
+ <option value="other">Other</option>
331
+ </select>
332
+ <div class="absolute inset-y-0 right-0 flex items-center pr-2 pointer-events-none">
333
+ <i class="fas fa-chevron-down text-gray-400"></i>
334
+ </div>
335
+ </div>
336
+ </div>
337
+
338
+ <div>
339
+ <label class="block text-sm font-medium text-gray-700 mb-1">Amount</label>
340
+ <input type="number" id="budgetAmount" class="w-full p-2 border border-gray-300 rounded-lg" placeholder="0.00">
341
+ </div>
342
+
343
+ <div>
344
+ <label class="block text-sm font-medium text-gray-700 mb-1">Period</label>
345
+ <div class="relative">
346
+ <select id="budgetPeriod" class="w-full p-2 border border-gray-300 rounded-lg appearance-none">
347
+ <option value="weekly">Weekly</option>
348
+ <option value="monthly">Monthly</option>
349
+ <option value="yearly">Yearly</option>
350
+ </select>
351
+ <div class="absolute inset-y-0 right-0 flex items-center pr-2 pointer-events-none">
352
+ <i class="fas fa-chevron-down text-gray-400"></i>
353
+ </div>
354
+ </div>
355
+ </div>
356
+
357
+ <button id="saveBudgetBtn" class="w-full bg-indigo-600 text-white py-2 rounded-lg">
358
+ Save Budget
359
+ </button>
360
+ </div>
361
+ </div>
362
+ </div>
363
+
364
+ <!-- Filter Modal -->
365
+ <div id="filterModal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center p-4 z-20 hidden">
366
+ <div class="bg-white rounded-lg w-full max-w-md max-h-[90vh] overflow-y-auto fade-in">
367
+ <div class="p-4 border-b border-gray-200 flex justify-between items-center">
368
+ <h3 class="font-bold text-lg">Filter Transactions</h3>
369
+ <button id="closeFilterModal" class="text-gray-500">
370
+ <i class="fas fa-times"></i>
371
+ </button>
372
+ </div>
373
+
374
+ <div class="p-4 space-y-4">
375
+ <div>
376
+ <label class="block text-sm font-medium text-gray-700 mb-1">Category</label>
377
+ <div class="relative">
378
+ <select id="filterCategory" class="w-full p-2 border border-gray-300 rounded-lg appearance-none">
379
+ <option value="all">All Categories</option>
380
+ <option value="food">Food</option>
381
+ <option value="transport">Transport</option>
382
+ <option value="bills">Bills</option>
383
+ <option value="shopping">Shopping</option>
384
+ <option value="entertainment">Entertainment</option>
385
+ <option value="other">Other</option>
386
+ </select>
387
+ <div class="absolute inset-y-0 right-0 flex items-center pr-2 pointer-events-none">
388
+ <i class="fas fa-chevron-down text-gray-400"></i>
389
+ </div>
390
+ </div>
391
+ </div>
392
+
393
+ <div>
394
+ <label class="block text-sm font-medium text-gray-700 mb-1">Type</label>
395
+ <div class="relative">
396
+ <select id="filterType" class="w-full p-2 border border-gray-300 rounded-lg appearance-none">
397
+ <option value="all">All Types</option>
398
+ <option value="expense">Expense</option>
399
+ <option value="income">Income</option>
400
+ </select>
401
+ <div class="absolute inset-y-0 right-0 flex items-center pr-2 pointer-events-none">
402
+ <i class="fas fa-chevron-down text-gray-400"></i>
403
+ </div>
404
+ </div>
405
+ </div>
406
+
407
+ <div class="grid grid-cols-2 gap-4">
408
+ <div>
409
+ <label class="block text-sm font-medium text-gray-700 mb-1">From Date</label>
410
+ <input type="date" id="filterFromDate" class="w-full p-2 border border-gray-300 rounded-lg">
411
+ </div>
412
+
413
+ <div>
414
+ <label class="block text-sm font-medium text-gray-700 mb-1">To Date</label>
415
+ <input type="date" id="filterToDate" class="w-full p-2 border border-gray-300 rounded-lg">
416
+ </div>
417
+ </div>
418
+
419
+ <div class="flex space-x-2">
420
+ <button id="resetFilterBtn" class="flex-1 bg-gray-200 text-gray-800 py-2 rounded-lg">
421
+ Reset
422
+ </button>
423
+ <button id="applyFilterBtn" class="flex-1 bg-indigo-600 text-white py-2 rounded-lg">
424
+ Apply
425
+ </button>
426
+ </div>
427
+ </div>
428
+ </div>
429
+ </div>
430
+
431
+ <!-- Sort Modal -->
432
+ <div id="sortModal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center p-4 z-20 hidden">
433
+ <div class="bg-white rounded-lg w-full max-w-md max-h-[90vh] overflow-y-auto fade-in">
434
+ <div class="p-4 border-b border-gray-200 flex justify-between items-center">
435
+ <h3 class="font-bold text-lg">Sort Transactions</h3>
436
+ <button id="closeSortModal" class="text-gray-500">
437
+ <i class="fas fa-times"></i>
438
+ </button>
439
+ </div>
440
+
441
+ <div class="p-4 space-y-3">
442
+ <div class="flex items-center justify-between p-2 border-b border-gray-100">
443
+ <span>Date (Newest First)</span>
444
+ <input type="radio" name="sortOption" value="dateDesc" checked class="h-4 w-4 text-indigo-600">
445
+ </div>
446
+
447
+ <div class="flex items-center justify-between p-2 border-b border-gray-100">
448
+ <span>Date (Oldest First)</span>
449
+ <input type="radio" name="sortOption" value="dateAsc" class="h-4 w-4 text-indigo-600">
450
+ </div>
451
+
452
+ <div class="flex items-center justify-between p-2 border-b border-gray-100">
453
+ <span>Amount (Highest First)</span>
454
+ <input type="radio" name="sortOption" value="amountDesc" class="h-4 w-4 text-indigo-600">
455
+ </div>
456
+
457
+ <div class="flex items-center justify-between p-2 border-b border-gray-100">
458
+ <span>Amount (Lowest First)</span>
459
+ <input type="radio" name="sortOption" value="amountAsc" class="h-4 w-4 text-indigo-600">
460
+ </div>
461
+
462
+ <div class="flex items-center justify-between p-2">
463
+ <span>Category (A-Z)</span>
464
+ <input type="radio" name="sortOption" value="categoryAsc" class="h-4 w-4 text-indigo-600">
465
+ </div>
466
+
467
+ <button id="applySortBtn" class="w-full mt-4 bg-indigo-600 text-white py-2 rounded-lg">
468
+ Apply Sort
469
+ </button>
470
+ </div>
471
+ </div>
472
+ </div>
473
+
474
+ <!-- Export Modal -->
475
+ <div id="exportModal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center p-4 z-20 hidden">
476
+ <div class="bg-white rounded-lg w-full max-w-md max-h-[90vh] overflow-y-auto fade-in">
477
+ <div class="p-4 border-b border-gray-200 flex justify-between items-center">
478
+ <h3 class="font-bold text-lg">Export Data</h3>
479
+ <button id="closeExportModal" class="text-gray-500">
480
+ <i class="fas fa-times"></i>
481
+ </button>
482
+ </div>
483
+
484
+ <div class="p-4 space-y-4">
485
+ <p class="text-sm text-gray-600">Export your financial data for backup or analysis.</p>
486
+
487
+ <div class="space-y-2">
488
+ <button id="exportJSONBtn" class="w-full bg-gray-100 text-gray-800 py-3 rounded-lg flex items-center justify-center space-x-2">
489
+ <i class="fas fa-file-code"></i>
490
+ <span>Export as JSON</span>
491
+ </button>
492
+
493
+ <button id="exportCSVBtn" class="w-full bg-gray-100 text-gray-800 py-3 rounded-lg flex items-center justify-center space-x-2">
494
+ <i class="fas fa-file-csv"></i>
495
+ <span>Export as CSV</span>
496
+ </button>
497
+
498
+ <button id="importBtn" class="w-full bg-gray-100 text-gray-800 py-3 rounded-lg flex items-center justify-center space-x-2">
499
+ <i class="fas fa-file-import"></i>
500
+ <span>Import Data</span>
501
+ </button>
502
+ </div>
503
+
504
+ <div class="hidden" id="importSection">
505
+ <label class="block text-sm font-medium text-gray-700 mb-1">Select file to import</label>
506
+ <input type="file" id="importFile" class="w-full p-2 border border-gray-300 rounded-lg">
507
+ <button id="confirmImportBtn" class="w-full mt-2 bg-indigo-600 text-white py-2 rounded-lg">
508
+ Confirm Import
509
+ </button>
510
+ </div>
511
+ </div>
512
+ </div>
513
+ </div>
514
+
515
+ <script>
516
+ // Initialize local storage if not exists
517
+ if (!localStorage.getItem('transactions')) {
518
+ localStorage.setItem('transactions', JSON.stringify([]));
519
+ }
520
+
521
+ if (!localStorage.getItem('goals')) {
522
+ localStorage.setItem('goals', JSON.stringify([]));
523
+ }
524
+
525
+ if (!localStorage.getItem('budgets')) {
526
+ localStorage.setItem('budgets', JSON.stringify([]));
527
+ }
528
+
529
+ if (!localStorage.getItem('settings')) {
530
+ localStorage.setItem('settings', JSON.stringify({
531
+ theme: 'light',
532
+ currency: '$'
533
+ }));
534
+ }
535
+
536
+ // DOM Elements
537
+ const dashboardView = document.getElementById('dashboardView');
538
+ const transactionsView = document.getElementById('transactionsView');
539
+ const budgetsView = document.getElementById('budgetsView');
540
+ const goalsView = document.getElementById('goalsView');
541
+
542
+ const dashboardTab = document.getElementById('dashboardTab');
543
+ const transactionsTab = document.getElementById('transactionsTab');
544
+ const budgetsTab = document.getElementById('budgetsTab');
545
+ const goalsTab = document.getElementById('goalsTab');
546
+
547
+ const transactionModal = document.getElementById('transactionModal');
548
+ const goalModal = document.getElementById('goalModal');
549
+ const budgetModal = document.getElementById('budgetModal');
550
+ const filterModal = document.getElementById('filterModal');
551
+ const sortModal = document.getElementById('sortModal');
552
+ const exportModal = document.getElementById('exportModal');
553
+
554
+ const addTransactionBtn = document.getElementById('addTransactionBtn');
555
+ const addTransactionBtnBottom = document.getElementById('addTransactionBtnBottom');
556
+ const addGoalBtn = document.getElementById('addGoalBtn');
557
+ const addGoalBtn2 = document.getElementById('addGoalBtn2');
558
+ const addBudgetBtn = document.getElementById('addBudgetBtn');
559
+ const filterBtn = document.getElementById('filterBtn');
560
+ const sortBtn = document.getElementById('sortBtn');
561
+ const exportBtn = document.getElementById('exportBtn');
562
+
563
+ const closeTransactionModal = document.getElementById('closeTransactionModal');
564
+ const closeGoalModal = document.getElementById('closeGoalModal');
565
+ const closeBudgetModal = document.getElementById('closeBudgetModal');
566
+ const closeFilterModal = document.getElementById('closeFilterModal');
567
+ const closeSortModal = document.getElementById('closeSortModal');
568
+ const closeExportModal = document.getElementById('closeExportModal');
569
+
570
+ const transactionAmount = document.getElementById('transactionAmount');
571
+ const transactionCategory = document.getElementById('transactionCategory');
572
+ const transactionDescription = document.getElementById('transactionDescription');
573
+ const transactionDate = document.getElementById('transactionDate');
574
+ const expenseBtn = document.getElementById('expenseBtn');
575
+ const incomeBtn = document.getElementById('incomeBtn');
576
+
577
+ const goalName = document.getElementById('goalName');
578
+ const goalTarget = document.getElementById('goalTarget');
579
+ const goalCurrent = document.getElementById('goalCurrent');
580
+ const goalDate = document.getElementById('goalDate');
581
+ const saveGoalBtn = document.getElementById('saveGoalBtn');
582
+
583
+ const budgetCategory = document.getElementById('budgetCategory');
584
+ const budgetAmount = document.getElementById('budgetAmount');
585
+ const budgetPeriod = document.getElementById('budgetPeriod');
586
+ const saveBudgetBtn = document.getElementById('saveBudgetBtn');
587
+
588
+ const filterCategory = document.getElementById('filterCategory');
589
+ const filterType = document.getElementById('filterType');
590
+ const filterFromDate = document.getElementById('filterFromDate');
591
+ const filterToDate = document.getElementById('filterToDate');
592
+ const resetFilterBtn = document.getElementById('resetFilterBtn');
593
+ const applyFilterBtn = document.getElementById('applyFilterBtn');
594
+
595
+ const applySortBtn = document.getElementById('applySortBtn');
596
+
597
+ const exportJSONBtn = document.getElementById('exportJSONBtn');
598
+ const exportCSVBtn = document.getElementById('exportCSVBtn');
599
+ const importBtn = document.getElementById('importBtn');
600
+ const importSection = document.getElementById('importSection');
601
+ const importFile = document.getElementById('importFile');
602
+ const confirmImportBtn = document.getElementById('confirmImportBtn');
603
+
604
+ const totalBalance = document.getElementById('totalBalance');
605
+ const monthlyExpense = document.getElementById('monthlyExpense');
606
+ const recentTransactions = document.getElementById('recentTransactions');
607
+ const allTransactions = document.getElementById('allTransactions');
608
+ const goalsList = document.getElementById('goalsList');
609
+ const fullGoalsList = document.getElementById('fullGoalsList');
610
+ const budgetsList = document.getElementById('budgetsList');
611
+
612
+ const themeToggle = document.getElementById('themeToggle');
613
+
614
+ // Chart
615
+ const expenseChartCtx = document.getElementById('expenseChart').getContext('2d');
616
+ let expenseChart;
617
+
618
+ // Current date for default values
619
+ const today = new Date();
620
+ const todayFormatted = today.toISOString().split('T')[0];
621
+ transactionDate.value = todayFormatted;
622
+ goalDate.valueAsDate = new Date(today.setMonth(today.getMonth() + 1));
623
+
624
+ // Event Listeners
625
+ dashboardTab.addEventListener('click', () => switchView('dashboard'));
626
+ transactionsTab.addEventListener('click', () => switchView('transactions'));
627
+ budgetsTab.addEventListener('click', () => switchView('budgets'));
628
+ goalsTab.addEventListener('click', () => switchView('goals'));
629
+
630
+ addTransactionBtn.addEventListener('click', () => transactionModal.classList.remove('hidden'));
631
+ addTransactionBtnBottom.addEventListener('click', () => transactionModal.classList.remove('hidden'));
632
+ addGoalBtn.addEventListener('click', () => goalModal.classList.remove('hidden'));
633
+ addGoalBtn2.addEventListener('click', () => goalModal.classList.remove('hidden'));
634
+ addBudgetBtn.addEventListener('click', () => budgetModal.classList.remove('hidden'));
635
+ filterBtn.addEventListener('click', () => filterModal.classList.remove('hidden'));
636
+ sortBtn.addEventListener('click', () => sortModal.classList.remove('hidden'));
637
+ exportBtn.addEventListener('click', () => exportModal.classList.remove('hidden'));
638
+
639
+ closeTransactionModal.addEventListener('click', () => transactionModal.classList.add('hidden'));
640
+ closeGoalModal.addEventListener('click', () => goalModal.classList.add('hidden'));
641
+ closeBudgetModal.addEventListener('click', () => budgetModal.classList.add('hidden'));
642
+ closeFilterModal.addEventListener('click', () => filterModal.classList.add('hidden'));
643
+ closeSortModal.addEventListener('click', () => sortModal.classList.add('hidden'));
644
+ closeExportModal.addEventListener('click', () => exportModal.classList.add('hidden'));
645
+
646
+ expenseBtn.addEventListener('click', () => saveTransaction('expense'));
647
+ incomeBtn.addEventListener('click', () => saveTransaction('income'));
648
+ saveGoalBtn.addEventListener('click', saveGoal);
649
+ saveBudgetBtn.addEventListener('click', saveBudget);
650
+
651
+ resetFilterBtn.addEventListener('click', resetFilters);
652
+ applyFilterBtn.addEventListener('click', applyFilters);
653
+ applySortBtn.addEventListener('click', applySort);
654
+
655
+ exportJSONBtn.addEventListener('click', exportJSON);
656
+ exportCSVBtn.addEventListener('click', exportCSV);
657
+ importBtn.addEventListener('click', () => importSection.classList.toggle('hidden'));
658
+ confirmImportBtn.addEventListener('click', importData);
659
+
660
+ themeToggle.addEventListener('click', toggleTheme);
661
+
662
+ // Initialize app
663
+ initApp();
664
+
665
+ // Functions
666
+ function initApp() {
667
+ // Load theme
668
+ const settings = JSON.parse(localStorage.getItem('settings'));
669
+ if (settings.theme === 'dark') {
670
+ document.body.classList.add('dark');
671
+ themeToggle.innerHTML = '<i class="fas fa-sun"></i>';
672
+ }
673
+
674
+ // Set currency
675
+ totalBalance.textContent = settings.currency + '0.00';
676
+ monthlyExpense.textContent = settings.currency + '0.00';
677
+
678
+ // Load data
679
+ updateDashboard();
680
+ loadTransactions();
681
+ loadGoals();
682
+ loadBudgets();
683
+ }
684
+
685
+ function switchView(view) {
686
+ dashboardView.classList.add('hidden');
687
+ transactionsView.classList.add('hidden');
688
+ budgetsView.classList.add('hidden');
689
+ goalsView.classList.add('hidden');
690
+
691
+ dashboardTab.classList.remove('text-indigo-600', 'border-indigo-600');
692
+ dashboardTab.classList.add('text-gray-500');
693
+ transactionsTab.classList.remove('text-indigo-600', 'border-indigo-600');
694
+ transactionsTab.classList.add('text-gray-500');
695
+ budgetsTab.classList.remove('text-indigo-600', 'border-indigo-600');
696
+ budgetsTab.classList.add('text-gray-500');
697
+ goalsTab.classList.remove('text-indigo-600', 'border-indigo-600');
698
+ goalsTab.classList.add('text-gray-500');
699
+
700
+ switch(view) {
701
+ case 'dashboard':
702
+ dashboardView.classList.remove('hidden');
703
+ dashboardTab.classList.remove('text-gray-500');
704
+ dashboardTab.classList.add('text-indigo-600', 'border-indigo-600');
705
+ break;
706
+ case 'transactions':
707
+ transactionsView.classList.remove('hidden');
708
+ transactionsTab.classList.remove('text-gray-500');
709
+ transactionsTab.classList.add('text-indigo-600', 'border-indigo-600');
710
+ break;
711
+ case 'budgets':
712
+ budgetsView.classList.remove('hidden');
713
+ budgetsTab.classList.remove('text-gray-500');
714
+ budgetsTab.classList.add('text-indigo-600', 'border-indigo-600');
715
+ break;
716
+ case 'goals':
717
+ goalsView.classList.remove('hidden');
718
+ goalsTab.classList.remove('text-gray-500');
719
+ goalsTab.classList.add('text-indigo-600', 'border-indigo-600');
720
+ break;
721
+ }
722
+ }
723
+
724
+ function saveTransaction(type) {
725
+ const amount = parseFloat(transactionAmount.value);
726
+ const category = transactionCategory.value;
727
+ const description = transactionDescription.value;
728
+ const date = transactionDate.value;
729
+
730
+ if (!amount || amount <= 0) {
731
+ alert('Please enter a valid amount');
732
+ return;
733
+ }
734
+
735
+ const transactions = JSON.parse(localStorage.getItem('transactions'));
736
+ const newTransaction = {
737
+ id: Date.now(),
738
+ amount: type === 'expense' ? -amount : amount,
739
+ category,
740
+ description,
741
+ date,
742
+ type
743
+ };
744
+
745
+ transactions.push(newTransaction);
746
+ localStorage.setItem('transactions', JSON.stringify(transactions));
747
+
748
+ // Reset form
749
+ transactionAmount.value = '';
750
+ transactionDescription.value = '';
751
+ transactionDate.value = todayFormatted;
752
+
753
+ // Close modal
754
+ transactionModal.classList.add('hidden');
755
+
756
+ // Update UI
757
+ updateDashboard();
758
+ loadTransactions();
759
+ }
760
+
761
+ function saveGoal() {
762
+ const name = goalName.value;
763
+ const target = parseFloat(goalTarget.value);
764
+ const current = parseFloat(goalCurrent.value);
765
+ const date = goalDate.value;
766
+
767
+ if (!name || !target || target <= 0 || current < 0) {
768
+ alert('Please fill all required fields with valid values');
769
+ return;
770
+ }
771
+
772
+ const goals = JSON.parse(localStorage.getItem('goals'));
773
+ const newGoal = {
774
+ id: Date.now(),
775
+ name,
776
+ target,
777
+ current,
778
+ date
779
+ };
780
+
781
+ goals.push(newGoal);
782
+ localStorage.setItem('goals', JSON.stringify(goals));
783
+
784
+ // Reset form
785
+ goalName.value = '';
786
+ goalTarget.value = '';
787
+ goalCurrent.value = '';
788
+ goalDate.valueAsDate = new Date(today.setMonth(today.getMonth() + 1));
789
+
790
+ // Close modal
791
+ goalModal.classList.add('hidden');
792
+
793
+ // Update UI
794
+ updateDashboard();
795
+ loadGoals();
796
+ }
797
+
798
+ function saveBudget() {
799
+ const category = budgetCategory.value;
800
+ const amount = parseFloat(budgetAmount.value);
801
+ const period = budgetPeriod.value;
802
+
803
+ if (!amount || amount <= 0) {
804
+ alert('Please enter a valid amount');
805
+ return;
806
+ }
807
+
808
+ const budgets = JSON.parse(localStorage.getItem('budgets'));
809
+
810
+ // Check if budget for this category already exists
811
+ const existingBudgetIndex = budgets.findIndex(b => b.category === category);
812
+
813
+ if (existingBudgetIndex !== -1) {
814
+ budgets[existingBudgetIndex] = {
815
+ category,
816
+ amount,
817
+ period
818
+ };
819
+ } else {
820
+ budgets.push({
821
+ category,
822
+ amount,
823
+ period
824
+ });
825
+ }
826
+
827
+ localStorage.setItem('budgets', JSON.stringify(budgets));
828
+
829
+ // Reset form
830
+ budgetAmount.value = '';
831
+
832
+ // Close modal
833
+ budgetModal.classList.add('hidden');
834
+
835
+ // Update UI
836
+ updateDashboard();
837
+ loadBudgets();
838
+ }
839
+
840
+ function updateDashboard() {
841
+ const transactions = JSON.parse(localStorage.getItem('transactions'));
842
+ const goals = JSON.parse(localStorage.getItem('goals'));
843
+ const budgets = JSON.parse(localStorage.getItem('budgets'));
844
+ const settings = JSON.parse(localStorage.getItem('settings'));
845
+
846
+ // Calculate total balance
847
+ const balance = transactions.reduce((sum, t) => sum + t.amount, 0);
848
+ totalBalance.textContent = settings.currency + balance.toFixed(2);
849
+
850
+ // Calculate monthly expenses
851
+ const currentMonth = new Date().getMonth();
852
+ const currentYear = new Date().getFullYear();
853
+
854
+ const monthlyExpenses = transactions
855
+ .filter(t => {
856
+ const date = new Date(t.date);
857
+ return date.getMonth() === currentMonth &&
858
+ date.getFullYear() === currentYear &&
859
+ t.type === 'expense';
860
+ })
861
+ .reduce((sum, t) => sum + Math.abs(t.amount), 0);
862
+
863
+ monthlyExpense.textContent = settings.currency + monthlyExpenses.toFixed(2);
864
+
865
+ // Update recent transactions (last 5)
866
+ recentTransactions.innerHTML = '';
867
+ const recent = [...transactions].sort((a, b) => new Date(b.date) - new Date(a.date)).slice(0, 5);
868
+
869
+ recent.forEach(t => {
870
+ const transactionEl = document.createElement('div');
871
+ transactionEl.className = 'flex justify-between items-center p-2 bg-gray-50 rounded-lg';
872
+
873
+ const iconMap = {
874
+ food: 'utensils',
875
+ transport: 'bus',
876
+ bills: 'file-invoice-dollar',
877
+ shopping: 'shopping-bag',
878
+ entertainment: 'film',
879
+ other: 'ellipsis-h'
880
+ };
881
+
882
+ transactionEl.innerHTML = `
883
+ <div class="flex items-center space-x-3">
884
+ <div class="p-2 rounded-full bg-${t.type === 'expense' ? 'red' : 'green'}-100 text-${t.type === 'expense' ? 'red' : 'green'}-600">
885
+ <i class="fas fa-${iconMap[t.category] || 'ellipsis-h'}"></i>
886
+ </div>
887
+ <div>
888
+ <p class="font-medium">${t.category.charAt(0).toUpperCase() + t.category.slice(1)}</p>
889
+ <p class="text-xs text-gray-500">${t.date} ${t.description ? '· ' + t.description : ''}</p>
890
+ </div>
891
+ </div>
892
+ <p class="font-medium text-${t.type === 'expense' ? 'red' : 'green'}-600">${t.type === 'expense' ? '-' : '+'}${settings.currency}${Math.abs(t.amount).toFixed(2)}</p>
893
+ `;
894
+
895
+ recentTransactions.appendChild(transactionEl);
896
+ });
897
+
898
+ // Update goals list
899
+ goalsList.innerHTML = '';
900
+ fullGoalsList.innerHTML = '';
901
+
902
+ goals.forEach(g => {
903
+ const progress = (g.current / g.target) * 100;
904
+ const daysLeft = Math.ceil((new Date(g.date) - new Date()) / (1000 * 60 * 60 * 24));
905
+
906
+ const goalEl = document.createElement('div');
907
+ goalEl.className = 'bg-white rounded-lg shadow p-4';
908
+
909
+ goalEl.innerHTML = `
910
+ <div class="flex justify-between items-start mb-2">
911
+ <h3 class="font-medium">${g.name}</h3>
912
+ <span class="text-sm ${daysLeft < 0 ? 'text-red-500' : 'text-gray-500'}">${daysLeft < 0 ? 'Expired' : daysLeft + ' days left'}</span>
913
+ </div>
914
+ <div class="flex justify-between text-sm mb-3">
915
+ <span>${settings.currency}${g.current.toFixed(2)}</span>
916
+ <span>${settings.currency}${g.target.toFixed(2)}</span>
917
+ </div>
918
+ <div class="w-full bg-gray-200 rounded-full h-2">
919
+ <div class="bg-indigo-600 h-2 rounded-full" style="width: ${Math.min(progress, 100)}%"></div>
920
+ </div>
921
+ <div class="flex justify-between mt-3">
922
+ <button class="text-xs text-indigo-600 edit-goal" data-id="${g.id}">
923
+ <i class="fas fa-edit mr-1"></i> Edit
924
+ </button>
925
+ <button class="text-xs text-indigo-600 delete-goal" data-id="${g.id}">
926
+ <i class="fas fa-trash mr-1"></i> Delete
927
+ </button>
928
+ </div>
929
+ `;
930
+
931
+ goalsList.appendChild(goalEl.cloneNode(true));
932
+ fullGoalsList.appendChild(goalEl);
933
+ });
934
+
935
+ // Add event listeners to goal buttons
936
+ document.querySelectorAll('.edit-goal').forEach(btn => {
937
+ btn.addEventListener('click', (e) => editGoal(e.target.getAttribute('data-id')));
938
+ });
939
+
940
+ document.querySelectorAll('.delete-goal').forEach(btn => {
941
+ btn.addEventListener('click', (e) => deleteGoal(e.target.getAttribute('data-id')));
942
+ });
943
+
944
+ // Update expense chart
945
+ updateExpenseChart();
946
+ }
947
+
948
+ function loadTransactions() {
949
+ const transactions = JSON.parse(localStorage.getItem('transactions'));
950
+ const settings = JSON.parse(localStorage.getItem('settings'));
951
+
952
+ allTransactions.innerHTML = '';
953
+
954
+ // Group transactions by date
955
+ const grouped = transactions.reduce((groups, t) => {
956
+ const date = t.date;
957
+ if (!groups[date]) {
958
+ groups[date] = [];
959
+ }
960
+ groups[date].push(t);
961
+ return groups;
962
+ }, {});
963
+
964
+ // Sort dates in descending order
965
+ const sortedDates = Object.keys(grouped).sort((a, b) => new Date(b) - new Date(a));
966
+
967
+ sortedDates.forEach(date => {
968
+ const dateHeader = document.createElement('div');
969
+ dateHeader.className = 'font-medium text-gray-500 mb-2';
970
+ dateHeader.textContent = formatDate(date);
971
+ allTransactions.appendChild(dateHeader);
972
+
973
+ grouped[date].forEach(t => {
974
+ const transactionEl = document.createElement('div');
975
+ transactionEl.className = 'flex justify-between items-center p-3 bg-gray-50 rounded-lg mb-2';
976
+
977
+ const iconMap = {
978
+ food: 'utensils',
979
+ transport: 'bus',
980
+ bills: 'file-invoice-dollar',
981
+ shopping: 'shopping-bag',
982
+ entertainment: 'film',
983
+ other: 'ellipsis-h'
984
+ };
985
+
986
+ transactionEl.innerHTML = `
987
+ <div class="flex items-center space-x-3">
988
+ <div class="p-2 rounded-full bg-${t.type === 'expense' ? 'red' : 'green'}-100 text-${t.type === 'expense' ? 'red' : 'green'}-600">
989
+ <i class="fas fa-${iconMap[t.category] || 'ellipsis-h'}"></i>
990
+ </div>
991
+ <div>
992
+ <p class="font-medium">${t.category.charAt(0).toUpperCase() + t.category.slice(1)}</p>
993
+ <p class="text-xs text-gray-500">${t.description || ''}</p>
994
+ </div>
995
+ </div>
996
+ <div class="text-right">
997
+ <p class="font-medium text-${t.type === 'expense' ? 'red' : 'green'}-600">${t.type === 'expense' ? '-' : '+'}${settings.currency}${Math.abs(t.amount).toFixed(2)}</p>
998
+ <div class="flex space-x-2 mt-1">
999
+ <button class="text-xs text-indigo-600 edit-transaction" data-id="${t.id}">
1000
+ <i class="fas fa-edit"></i>
1001
+ </button>
1002
+ <button class="text-xs text-red-600 delete-transaction" data-id="${t.id}">
1003
+ <i class="fas fa-trash"></i>
1004
+ </button>
1005
+ </div>
1006
+ </div>
1007
+ `;
1008
+
1009
+ allTransactions.appendChild(transactionEl);
1010
+ });
1011
+ });
1012
+
1013
+ // Add event listeners to transaction buttons
1014
+ document.querySelectorAll('.edit-transaction').forEach(btn => {
1015
+ btn.addEventListener('click', (e) => editTransaction(e.target.getAttribute('data-id')));
1016
+ });
1017
+
1018
+ document.querySelectorAll('.delete-transaction').forEach(btn => {
1019
+ btn.addEventListener('click', (e) => deleteTransaction(e.target.getAttribute('data-id')));
1020
+ });
1021
+ }
1022
+
1023
+ function loadGoals() {
1024
+ updateDashboard(); // Goals are already handled in updateDashboard
1025
+ }
1026
+
1027
+ function loadBudgets() {
1028
+ const budgets = JSON.parse(localStorage.getItem('budgets'));
1029
+ const transactions = JSON.parse(localStorage.getItem('transactions'));
1030
+ const settings = JSON.parse(localStorage.getItem('settings'));
1031
+
1032
+ budgetsList.innerHTML = '';
1033
+
1034
+ // Calculate current month's expenses per category
1035
+ const currentMonth = new Date().getMonth();
1036
+ const currentYear = new Date().getFullYear();
1037
+
1038
+ const monthlyExpensesByCategory = transactions
1039
+ .filter(t => {
1040
+ const date = new Date(t.date);
1041
+ return date.getMonth() === currentMonth &&
1042
+ date.getFullYear() === currentYear &&
1043
+ t.type === 'expense';
1044
+ })
1045
+ .reduce((acc, t) => {
1046
+ if (!acc[t.category]) {
1047
+ acc[t.category] = 0;
1048
+ }
1049
+ acc[t.category] += Math.abs(t.amount);
1050
+ return acc;
1051
+ }, {});
1052
+
1053
+ budgets.forEach(b => {
1054
+ const spent = monthlyExpensesByCategory[b.category] || 0;
1055
+ const remaining = b.amount - spent;
1056
+ const progress = (spent / b.amount) * 100;
1057
+
1058
+ const budgetEl = document.createElement('div');
1059
+ budgetEl.className = 'bg-white rounded-lg shadow p-4';
1060
+
1061
+ budgetEl.innerHTML = `
1062
+ <div class="flex justify-between items-center mb-2">
1063
+ <h3 class="font-medium">${b.category.charAt(0).toUpperCase() + b.category.slice(1)}</h3>
1064
+ <span class="text-sm">${settings.currency}${b.amount.toFixed(2)} ${b.period}</span>
1065
+ </div>
1066
+ <div class="flex justify-between text-sm mb-3">
1067
+ <span>${settings.currency}${spent.toFixed(2)} spent</span>
1068
+ <span class="${remaining < 0 ? 'text-red-500' : 'text-green-500'}">${remaining < 0 ? 'Over by ' + settings.currency + Math.abs(remaining).toFixed(2) : settings.currency + remaining.toFixed(2) + ' left'}</span>
1069
+ </div>
1070
+ <div class="w-full bg-gray-200 rounded-full h-2">
1071
+ <div class="${progress > 100 ? 'bg-red-500' : 'bg-indigo-600'} h-2 rounded-full" style="width: ${Math.min(progress, 100)}%"></div>
1072
+ </div>
1073
+ <div class="flex justify-end mt-3">
1074
+ <button class="text-xs text-indigo-600 delete-budget" data-category="${b.category}">
1075
+ <i class="fas fa-trash mr-1"></i> Delete
1076
+ </button>
1077
+ </div>
1078
+ `;
1079
+
1080
+ budgetsList.appendChild(budgetEl);
1081
+ });
1082
+
1083
+ // Add event listeners to budget buttons
1084
+ document.querySelectorAll('.delete-budget').forEach(btn => {
1085
+ btn.addEventListener('click', (e) => deleteBudget(e.target.getAttribute('data-category')));
1086
+ });
1087
+ }
1088
+
1089
+ function updateExpenseChart() {
1090
+ const transactions = JSON.parse(localStorage.getItem('transactions'));
1091
+ const currentMonth = new Date().getMonth();
1092
+ const currentYear = new Date().getFullYear();
1093
+
1094
+ // Filter current month's expenses
1095
+ const monthlyExpenses = transactions.filter(t => {
1096
+ const date = new Date(t.date);
1097
+ return date.getMonth() === currentMonth &&
1098
+ date.getFullYear() === currentYear &&
1099
+ t.type === 'expense';
1100
+ });
1101
+
1102
+ // Group by category
1103
+ const expensesByCategory = monthlyExpenses.reduce((acc, t) => {
1104
+ if (!acc[t.category]) {
1105
+ acc[t.category] = 0;
1106
+ }
1107
+ acc[t.category] += Math.abs(t.amount);
1108
+ return acc;
1109
+ }, {});
1110
+
1111
+ const categories = Object.keys(expensesByCategory);
1112
+ const amounts = Object.values(expensesByCategory);
1113
+
1114
+ const colors = [
1115
+ '#FF6384', '#36A2EB', '#FFCE56', '#4BC0C0', '#9966FF', '#FF9F40'
1116
+ ];
1117
+
1118
+ // Destroy previous chart if exists
1119
+ if (expenseChart) {
1120
+ expenseChart.destroy();
1121
+ }
1122
+
1123
+ expenseChart = new Chart(expenseChartCtx, {
1124
+ type: 'pie',
1125
+ data: {
1126
+ labels: categories.map(c => c.charAt(0).toUpperCase() + c.slice(1)),
1127
+ datasets: [{
1128
+ data: amounts,
1129
+ backgroundColor: colors,
1130
+ borderWidth: 1
1131
+ }]
1132
+ },
1133
+ options: {
1134
+ responsive: true,
1135
+ maintainAspectRatio: false,
1136
+ plugins: {
1137
+ legend: {
1138
+ position: 'right',
1139
+ },
1140
+ tooltip: {
1141
+ callbacks: {
1142
+ label: function(context) {
1143
+ const label = context.label || '';
1144
+ const value = context.raw || 0;
1145
+ const total = context.dataset.data.reduce((a, b) => a + b, 0);
1146
+ const percentage = Math.round((value / total) * 100);
1147
+ return `${label}: ${percentage}% (${value.toFixed(2)})`;
1148
+ }
1149
+ }
1150
+ }
1151
+ }
1152
+ }
1153
+ });
1154
+ }
1155
+
1156
+ function editTransaction(id) {
1157
+ const transactions = JSON.parse(localStorage.getItem('transactions'));
1158
+ const transaction = transactions.find(t => t.id == id);
1159
+
1160
+ if (!transaction) return;
1161
+
1162
+ // Fill the form
1163
+ transactionAmount.value = Math.abs(transaction.amount);
1164
+ transactionCategory.value = transaction.category;
1165
+ transactionDescription.value = transaction.description || '';
1166
+ transactionDate.value = transaction.date;
1167
+
1168
+ // Show modal
1169
+ transactionModal.classList.remove('hidden');
1170
+
1171
+ // Remove the old transaction when saving
1172
+ expenseBtn.onclick = function() {
1173
+ const newTransaction = {
1174
+ id: transaction.id,
1175
+ amount: -parseFloat(transactionAmount.value),
1176
+ category: transactionCategory.value,
1177
+ description: transactionDescription.value,
1178
+ date: transactionDate.value,
1179
+ type: 'expense'
1180
+ };
1181
+
1182
+ const updatedTransactions = transactions.map(t => t.id == id ? newTransaction : t);
1183
+ localStorage.setItem('transactions', JSON.stringify(updatedTransactions));
1184
+
1185
+ transactionModal.classList.add('hidden');
1186
+ updateDashboard();
1187
+ loadTransactions();
1188
+ };
1189
+
1190
+ incomeBtn.onclick = function() {
1191
+ const newTransaction = {
1192
+ id: transaction.id,
1193
+ amount: parseFloat(transactionAmount.value),
1194
+ category: transactionCategory.value,
1195
+ description: transactionDescription.value,
1196
+ date: transactionDate.value,
1197
+ type: 'income'
1198
+ };
1199
+
1200
+ const updatedTransactions = transactions.map(t => t.id == id ? newTransaction : t);
1201
+ localStorage.setItem('transactions', JSON.stringify(updatedTransactions));
1202
+
1203
+ transactionModal.classList.add('hidden');
1204
+ updateDashboard();
1205
+ loadTransactions();
1206
+ };
1207
+ }
1208
+
1209
+ function deleteTransaction(id) {
1210
+ if (confirm('Are you sure you want to delete this transaction?')) {
1211
+ const transactions = JSON.parse(localStorage.getItem('transactions'));
1212
+ const updatedTransactions = transactions.filter(t => t.id != id);
1213
+ localStorage.setItem('transactions', JSON.stringify(updatedTransactions));
1214
+
1215
+ updateDashboard();
1216
+ loadTransactions();
1217
+ }
1218
+ }
1219
+
1220
+ function editGoal(id) {
1221
+ const goals = JSON.parse(localStorage.getItem('goals'));
1222
+ const goal = goals.find(g => g.id == id);
1223
+
1224
+ if (!goal) return;
1225
+
1226
+ // Fill the form
1227
+ goalName.value = goal.name;
1228
+ goalTarget.value = goal.target;
1229
+ goalCurrent.value = goal.current;
1230
+ goalDate.value = goal.date;
1231
+
1232
+ // Show modal
1233
+ goalModal.classList.remove('hidden');
1234
+
1235
+ // Update save button to edit instead of add
1236
+ saveGoalBtn.onclick = function() {
1237
+ const name = goalName.value;
1238
+ const target = parseFloat(goalTarget.value);
1239
+ const current = parseFloat(goalCurrent.value);
1240
+ const date = goalDate.value;
1241
+
1242
+ if (!name || !target || target <= 0 || current < 0) {
1243
+ alert('Please fill all required fields with valid values');
1244
+ return;
1245
+ }
1246
+
1247
+ const updatedGoal = {
1248
+ id: goal.id,
1249
+ name,
1250
+ target,
1251
+ current,
1252
+ date
1253
+ };
1254
+
1255
+ const updatedGoals = goals.map(g => g.id == id ? updatedGoal : g);
1256
+ localStorage.setItem('goals', JSON.stringify(updatedGoals));
1257
+
1258
+ goalModal.classList.add('hidden');
1259
+ updateDashboard();
1260
+ };
1261
+ }
1262
+
1263
+ function deleteGoal(id) {
1264
+ if (confirm('Are you sure you want to delete this goal?')) {
1265
+ const goals = JSON.parse(localStorage.getItem('goals'));
1266
+ const updatedGoals = goals.filter(g => g.id != id);
1267
+ localStorage.setItem('goals', JSON.stringify(updatedGoals));
1268
+
1269
+ updateDashboard();
1270
+ }
1271
+ }
1272
+
1273
+ function deleteBudget(category) {
1274
+ if (confirm('Are you sure you want to delete this budget?')) {
1275
+ const budgets = JSON.parse(localStorage.getItem('budgets'));
1276
+ const updatedBudgets = budgets.filter(b => b.category != category);
1277
+ localStorage.setItem('budgets', JSON.stringify(updatedBudgets));
1278
+
1279
+ loadBudgets();
1280
+ }
1281
+ }
1282
+
1283
+ function resetFilters() {
1284
+ filterCategory.value = 'all';
1285
+ filterType.value = 'all';
1286
+ filterFromDate.value = '';
1287
+ filterToDate.value = '';
1288
+ }
1289
+
1290
+ function applyFilters() {
1291
+ const category = filterCategory.value;
1292
+ const type = filterType.value;
1293
+ const fromDate = filterFromDate.value;
1294
+ const toDate = filterToDate.value;
1295
+
1296
+ // Save filter settings
1297
+ const settings = JSON.parse(localStorage.getItem('settings'));
1298
+ settings.filters = { category, type, fromDate, toDate };
1299
+ localStorage.setItem('settings', JSON.stringify(settings));
1300
+
1301
+ // Close modal
1302
+ filterModal.classList.add('hidden');
1303
+
1304
+ // Reload transactions with filters
1305
+ loadTransactions();
1306
+ }
1307
+
1308
+ function applySort() {
1309
+ const sortOption = document.querySelector('input[name="sortOption"]:checked').value;
1310
+
1311
+ // Save sort settings
1312
+ const settings = JSON.parse(localStorage.getItem('settings'));
1313
+ settings.sort = sortOption;
1314
+ localStorage.setItem('settings', JSON.stringify(settings));
1315
+
1316
+ // Close modal
1317
+ sortModal.classList.add('hidden');
1318
+
1319
+ // Reload transactions with sort
1320
+ loadTransactions();
1321
+ }
1322
+
1323
+ function exportJSON() {
1324
+ const transactions = JSON.parse(localStorage.getItem('transactions'));
1325
+ const goals = JSON.parse(localStorage.getItem('goals'));
1326
+ const budgets = JSON.parse(localStorage.getItem('budgets'));
1327
+
1328
+ const data = {
1329
+ transactions,
1330
+ goals,
1331
+ budgets,
1332
+ exportedAt: new Date().toISOString()
1333
+ };
1334
+
1335
+ const blob = new Blob([JSON.stringify(data, null, 2)], { type: 'application/json' });
1336
+ const url = URL.createObjectURL(blob);
1337
+
1338
+ const a = document.createElement('a');
1339
+ a.href = url;
1340
+ a.download = `fintrack-export-${new Date().toISOString().split('T')[0]}.json`;
1341
+ document.body.appendChild(a);
1342
+ a.click();
1343
+ document.body.removeChild(a);
1344
+ URL.revokeObjectURL(url);
1345
+
1346
+ exportModal.classList.add('hidden');
1347
+ }
1348
+
1349
+ function exportCSV() {
1350
+ const transactions = JSON.parse(localStorage.getItem('transactions'));
1351
+ const goals = JSON.parse(localStorage.getItem('goals'));
1352
+ const budgets = JSON.parse(localStorage.getItem('budgets'));
1353
+
1354
+ // Transactions CSV
1355
+ let transactionsCSV = 'Type,Amount,Category,Description,Date\n';
1356
+ transactions.forEach(t => {
1357
+ transactionsCSV += `${t.type},${t.amount},${t.category},${t.description || ''},${t.date}\n`;
1358
+ });
1359
+
1360
+ // Goals CSV
1361
+ let goalsCSV = 'Name,Target,Current,Date\n';
1362
+ goals.forEach(g => {
1363
+ goalsCSV += `${g.name},${g.target},${g.current},${g.date}\n`;
1364
+ });
1365
+
1366
+ // Budgets CSV
1367
+ let budgetsCSV = 'Category,Amount,Period\n';
1368
+ budgets.forEach(b => {
1369
+ budgetsCSV += `${b.category},${b.amount},${b.period}\n`;
1370
+ });
1371
+
1372
+ const fullCSV = `=== TRANSACTIONS ===\n${transactionsCSV}\n=== GOALS ===\n${goalsCSV}\n=== BUDGETS ===\n${budgetsCSV}`;
1373
+
1374
+ const blob = new Blob([fullCSV], { type: 'text/csv' });
1375
+ const url = URL.createObjectURL(blob);
1376
+
1377
+ const a = document.createElement('a');
1378
+ a.href = url;
1379
+ a.download = `fintrack-export-${new Date().toISOString().split('T')[0]}.csv`;
1380
+ document.body.appendChild(a);
1381
+ a.click();
1382
+ document.body.removeChild(a);
1383
+ URL.revokeObjectURL(url);
1384
+
1385
+ exportModal.classList.add('hidden');
1386
+ }
1387
+
1388
+ function importData() {
1389
+ const file = importFile.files[0];
1390
+ if (!file) {
1391
+ alert('Please select a file to import');
1392
+ return;
1393
+ }
1394
+
1395
+ const reader = new FileReader();
1396
+ reader.onload = function(e) {
1397
+ try {
1398
+ const data = JSON.parse(e.target.result);
1399
+
1400
+ if (confirm('This will overwrite your current data. Continue?')) {
1401
+ if (data.transactions) {
1402
+ localStorage.setItem('transactions', JSON.stringify(data.transactions));
1403
+ }
1404
+
1405
+ if (data.goals) {
1406
+ localStorage.setItem('goals', JSON.stringify(data.goals));
1407
+ }
1408
+
1409
+ if (data.budgets) {
1410
+ localStorage.setItem('budgets', JSON.stringify(data.budgets));
1411
+ }
1412
+
1413
+ alert('Data imported successfully!');
1414
+ initApp();
1415
+ exportModal.classList.add('hidden');
1416
+ }
1417
+ } catch (error) {
1418
+ alert('Error parsing the file. Please make sure it\'s a valid JSON export from FinTrack.');
1419
+ }
1420
+ };
1421
+ reader.readAsText(file);
1422
+ }
1423
+
1424
+ function toggleTheme() {
1425
+ const settings = JSON.parse(localStorage.getItem('settings'));
1426
+
1427
+ if (document.body.classList.contains('dark')) {
1428
+ document.body.classList.remove('dark');
1429
+ themeToggle.innerHTML = '<i class="fas fa-moon"></i>';
1430
+ settings.theme = 'light';
1431
+ } else {
1432
+ document.body.classList.add('dark');
1433
+ themeToggle.innerHTML = '<i class="fas fa-sun"></i>';
1434
+ settings.theme = 'dark';
1435
+ }
1436
+
1437
+ localStorage.setItem('settings', JSON.stringify(settings));
1438
+ }
1439
+
1440
+ function formatDate(dateString) {
1441
+ const date = new Date(dateString);
1442
+ return date.toLocaleDateString('en-US', {
1443
+ weekday: 'long',
1444
+ year: 'numeric',
1445
+ month: 'long',
1446
+ day: 'numeric'
1447
+ });
1448
+ }
1449
+ </script>
1450
+ <p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=nameadarsh/trial-space" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
1451
+ </html>