Digiator commited on
Commit
47fc765
·
verified ·
1 Parent(s): 824007c

Add 2 files

Browse files
Files changed (2) hide show
  1. README.md +6 -4
  2. index.html +1593 -19
README.md CHANGED
@@ -1,10 +1,12 @@
1
  ---
2
- title: Life Planner
3
- emoji: 👁
4
  colorFrom: blue
5
- colorTo: red
6
  sdk: static
7
  pinned: false
 
 
8
  ---
9
 
10
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
1
  ---
2
+ title: life-planner
3
+ emoji: 🐳
4
  colorFrom: blue
5
+ colorTo: blue
6
  sdk: static
7
  pinned: false
8
+ tags:
9
+ - deepsite
10
  ---
11
 
12
+ Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
index.html CHANGED
@@ -1,19 +1,1593 @@
1
- <!doctype html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width" />
6
- <title>My static Space</title>
7
- <link rel="stylesheet" href="style.css" />
8
- </head>
9
- <body>
10
- <div class="card">
11
- <h1>Welcome to your static Space!</h1>
12
- <p>You can modify this app directly by editing <i>index.html</i> in the Files and versions tab.</p>
13
- <p>
14
- Also don't forget to check the
15
- <a href="https://huggingface.co/docs/hub/spaces" target="_blank">Spaces documentation</a>.
16
- </p>
17
- </div>
18
- </body>
19
- </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
+ <title>Life Compass - Your Complete Life Planner</title>
7
+ <script src="https://cdn.tailwindcss.com"></script>
8
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
9
+ <style>
10
+ @import url('https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&display=swap');
11
+
12
+ body {
13
+ font-family: 'Poppins', sans-serif;
14
+ background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
15
+ min-height: 100vh;
16
+ }
17
+
18
+ .card {
19
+ transition: all 0.3s ease;
20
+ }
21
+
22
+ .card:hover {
23
+ transform: translateY(-5px);
24
+ box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
25
+ }
26
+
27
+ .section-title {
28
+ position: relative;
29
+ display: inline-block;
30
+ }
31
+
32
+ .section-title:after {
33
+ content: '';
34
+ position: absolute;
35
+ width: 100%;
36
+ height: 4px;
37
+ bottom: -5px;
38
+ left: 0;
39
+ background: linear-gradient(90deg, #3b82f6, #8b5cf6);
40
+ border-radius: 2px;
41
+ }
42
+
43
+ /* Pulse animation */
44
+ @keyframes pulse {
45
+ 0% { transform: scale(1); }
46
+ 50% { transform: scale(1.05); }
47
+ 100% { transform: scale(1); }
48
+ }
49
+ .pulse {
50
+ animation: pulse 2s infinite;
51
+ }
52
+
53
+ /* Fade-in animation */
54
+ @keyframes fadeIn {
55
+ from { opacity: 0; transform: translateY(10px); }
56
+ to { opacity: 1; transform: translateY(0); }
57
+ }
58
+ .animate-fade-in {
59
+ animation: fadeIn 0.5s ease-out forwards;
60
+ }
61
+
62
+ /* Modal backdrop */
63
+ .modal-backdrop {
64
+ background-color: rgba(0, 0, 0, 0.5);
65
+ }
66
+
67
+ /* Custom scrollbar */
68
+ ::-webkit-scrollbar {
69
+ width: 8px;
70
+ }
71
+ ::-webkit-scrollbar-track {
72
+ background: #f1f1f1;
73
+ border-radius: 4px;
74
+ }
75
+ ::-webkit-scrollbar-thumb {
76
+ background: #c1c1c1;
77
+ border-radius: 4px;
78
+ }
79
+ ::-webkit-scrollbar-thumb:hover {
80
+ background: #a1a1a1;
81
+ }
82
+ </style>
83
+ </head>
84
+ <body class="pb-20">
85
+ <!-- Header -->
86
+ <header class="bg-gradient-to-r from-blue-500 to-purple-600 text-white shadow-lg">
87
+ <div class="container mx-auto px-4 py-6">
88
+ <div class="flex flex-col md:flex-row justify-between items-center">
89
+ <div class="mb-4 md:mb-0">
90
+ <h1 class="text-3xl font-bold">Life Compass</h1>
91
+ <p class="text-blue-100 mt-1 text-sm">Your complete life navigation system</p>
92
+ </div>
93
+ <div class="flex items-center space-x-2">
94
+ <button id="export-btn" class="bg-white/10 hover:bg-white/20 px-3 py-1 rounded-full text-sm font-medium transition flex items-center">
95
+ <i class="fas fa-file-export mr-1"></i>Export
96
+ </button>
97
+ <button id="import-btn" class="bg-white/10 hover:bg-white/20 px-3 py-1 rounded-full text-sm font-medium transition flex items-center">
98
+ <i class="fas fa-file-import mr-1"></i>Import
99
+ </button>
100
+ <button id="profile-btn" class="bg-white text-blue-600 px-3 py-1 rounded-full text-sm font-medium hover:bg-blue-50 transition flex items-center">
101
+ <i class="fas fa-user mr-1"></i>Profile
102
+ </button>
103
+ </div>
104
+ </div>
105
+ </div>
106
+ </header>
107
+
108
+ <!-- Main Content -->
109
+ <main class="container mx-auto px-4 mt-8">
110
+ <!-- Welcome Section -->
111
+ <section class="mb-10">
112
+ <div class="bg-white rounded-xl shadow-md p-6 relative overflow-hidden">
113
+ <div class="absolute -right-16 -top-16 w-48 h-48 bg-purple-200 rounded-full opacity-20"></div>
114
+ <div class="absolute -left-16 -bottom-16 w-48 h-48 bg-blue-200 rounded-full opacity-20"></div>
115
+ <div class="relative z-10">
116
+ <h2 class="text-2xl font-bold text-gray-800 mb-3">Welcome to Your Life Planner</h2>
117
+ <p class="text-gray-600 mb-4 text-sm max-w-3xl">Organize every aspect of your life in one place. Track your progress, set goals, and achieve balance across all important areas.</p>
118
+ <div class="flex flex-wrap gap-3">
119
+ <button id="add-category-btn" class="bg-blue-600 hover:bg-blue-700 text-white px-4 py-2 rounded-lg font-medium transition flex items-center text-sm">
120
+ <i class="fas fa-plus mr-1"></i> Add New Category
121
+ </button>
122
+ <button id="view-all-btn" class="border border-blue-600 text-blue-600 hover:bg-blue-50 px-4 py-2 rounded-lg font-medium transition flex items-center text-sm">
123
+ <i class="fas fa-list mr-1"></i> View All Goals
124
+ </button>
125
+ </div>
126
+ </div>
127
+ </div>
128
+ </section>
129
+
130
+ <!-- Life Categories Grid -->
131
+ <section class="mb-12">
132
+ <div class="flex justify-between items-center mb-6">
133
+ <h2 class="text-xl font-bold text-gray-800 section-title">Life Categories</h2>
134
+ <div class="relative">
135
+ <input id="category-search" type="text" placeholder="Search categories..." class="py-1 px-3 pr-8 rounded-full text-sm bg-white border border-gray-300 placeholder-gray-400 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent">
136
+ <i class="fas fa-search absolute right-3 top-2 text-gray-400"></i>
137
+ </div>
138
+ </div>
139
+
140
+ <div id="categories-container" class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
141
+ <!-- Categories will be dynamically added here -->
142
+ </div>
143
+ </section>
144
+
145
+ <!-- Recent Activity -->
146
+ <section class="mb-12">
147
+ <h2 class="text-xl font-bold text-gray-800 mb-6 section-title">Recent Activity</h2>
148
+
149
+ <div class="bg-white rounded-xl shadow-md p-6">
150
+ <div class="grid grid-cols-1 md:grid-cols-3 gap-6">
151
+ <!-- Recent Updates -->
152
+ <div class="border-r border-gray-200 pr-6">
153
+ <h3 class="text-base font-semibold text-gray-700 mb-3 flex items-center">
154
+ <i class="fas fa-bell text-yellow-500 mr-2"></i> Recent Updates
155
+ </h3>
156
+ <ul id="recent-updates" class="space-y-3 max-h-48 overflow-y-auto pr-2">
157
+ <!-- Updates will be added here dynamically -->
158
+ </ul>
159
+ </div>
160
+
161
+ <!-- Quick Actions -->
162
+ <div class="border-r border-gray-200 pr-6">
163
+ <h3 class="text-base font-semibold text-gray-700 mb-3 flex items-center">
164
+ <i class="fas fa-bolt text-blue-500 mr-2"></i> Quick Actions
165
+ </h3>
166
+ <div class="grid grid-cols-2 gap-3">
167
+ <button id="quick-add-goal" class="bg-blue-50 text-blue-600 p-2 rounded-lg text-xs font-medium hover:bg-blue-100 transition flex flex-col items-center">
168
+ <i class="fas fa-bullseye text-sm mb-1"></i>
169
+ <span>Add Goal</span>
170
+ </button>
171
+ <button id="quick-view-tasks" class="bg-purple-50 text-purple-600 p-2 rounded-lg text-xs font-medium hover:bg-purple-100 transition flex flex-col items-center">
172
+ <i class="fas fa-tasks text-sm mb-1"></i>
173
+ <span>View Tasks</span>
174
+ </button>
175
+ <button id="quick-view-progress" class="bg-green-50 text-green-600 p-2 rounded-lg text-xs font-medium hover:bg-green-100 transition flex flex-col items-center">
176
+ <i class="fas fa-chart-line text-sm mb-1"></i>
177
+ <span>Progress</span>
178
+ </button>
179
+ <button id="quick-view-calendar" class="bg-pink-50 text-pink-600 p-2 rounded-lg text-xs font-medium hover:bg-pink-100 transition flex flex-col items-center">
180
+ <i class="fas fa-calendar-alt text-sm mb-1"></i>
181
+ <span>Calendar</span>
182
+ </button>
183
+ </div>
184
+ <div class="mt-4">
185
+ <h4 class="text-xs uppercase font-semibold text-gray-500 mb-2">Shortcuts</h4>
186
+ <div class="flex flex-wrap gap-2">
187
+ <button class="bg-gray-100 text-gray-700 px-2 py-1 rounded-full text-xs hover:bg-gray-200 transition">
188
+ <i class="fas fa-utensils mr-1"></i>Food
189
+ </button>
190
+ <button class="bg-gray-100 text-gray-700 px-2 py-1 rounded-full text-xs hover:bg-gray-200 transition">
191
+ <i class="fas fa-dumbbell mr-1"></i>Workout
192
+ </button>
193
+ <button class="bg-gray-100 text-gray-700 px-2 py-1 rounded-full text-xs hover:bg-gray-200 transition">
194
+ <i class="fas fa-book mr-1"></i>Reading
195
+ </button>
196
+ <button class="bg-gray-100 text-gray-700 px-2 py-1 rounded-full text-xs hover:bg-gray-200 transition">
197
+ <i class="fas fa-money-bill-wave mr-1"></i>Finance
198
+ </button>
199
+ </div>
200
+ </div>
201
+ </div>
202
+
203
+ <!-- Progress Overview -->
204
+ <div>
205
+ <h3 class="text-base font-semibold text-gray-700 mb-3 flex items-center">
206
+ <i class="fas fa-chart-pie text-green-500 mr-2"></i> Life Balance
207
+ </h3>
208
+ <div class="flex justify-center">
209
+ <div class="relative w-40 h-40">
210
+ <div id="balance-chart" class="w-full h-full"></div>
211
+ <div class="absolute inset-0 flex items-center justify-center">
212
+ <div class="text-center">
213
+ <p id="balance-percentage" class="text-2xl font-bold">0%</p>
214
+ <p class="text-xs text-gray-500">Balance</p>
215
+ </div>
216
+ </div>
217
+ </div>
218
+ </div>
219
+ <div id="balance-legend" class="mt-4 grid grid-cols-2 gap-2">
220
+ <!-- Legend items will be added here -->
221
+ </div>
222
+ </div>
223
+ </div>
224
+ </div>
225
+ </section>
226
+ </main>
227
+
228
+ <!-- Footer -->
229
+ <footer class="bg-gray-800 text-white py-8">
230
+ <div class="container mx-auto px-4">
231
+ <div class="grid grid-cols-1 md:grid-cols-3 gap-8">
232
+ <div>
233
+ <h3 class="text-lg font-bold mb-3">Life Compass</h3>
234
+ <p class="text-gray-400 text-sm">Your complete life navigation system to achieve balance and fulfillment.</p>
235
+ <div class="flex space-x-3 mt-3">
236
+ <a href="#" class="text-gray-400 hover:text-white"><i class="fab fa-twitter"></i></a>
237
+ <a href="#" class="text-gray-400 hover:text-white"><i class="fab fa-instagram"></i></a>
238
+ <a href="#" class="text-gray-400 hover:text-white"><i class="fab fa-github"></i></a>
239
+ </div>
240
+ </div>
241
+ <div>
242
+ <h4 class="font-semibold mb-3 text-sm">Quick Links</h4>
243
+ <ul class="space-y-2 text-sm">
244
+ <li><a href="#" class="text-gray-400 hover:text-white">Features</a></li>
245
+ <li><a href="#" class="text-gray-400 hover:text-white">Tips & Tricks</a></li>
246
+ <li><a href="#" class="text-gray-400 hover:text-white">Support</a></li>
247
+ </ul>
248
+ </div>
249
+ <div>
250
+ <h4 class="font-semibold mb-3 text-sm">Data Management</h4>
251
+ <div class="space-y-2">
252
+ <button id="reset-data-btn" class="text-gray-400 hover:text-white text-left text-sm w-full">
253
+ <i class="fas fa-trash-alt mr-1"></i> Reset All Data
254
+ </button>
255
+ <button id="backup-data-btn" class="text-gray-400 hover:text-white text-left text-sm w-full">
256
+ <i class="fas fa-save mr-1"></i> Backup Data
257
+ </button>
258
+ </div>
259
+ </div>
260
+ </div>
261
+ <div class="border-t border-gray-700 mt-8 pt-6 text-center text-gray-400 text-sm">
262
+ <p>&copy; 2023 Life Compass. All rights reserved.</p>
263
+ </div>
264
+ </div>
265
+ </footer>
266
+
267
+ <!-- Floating Action Button -->
268
+ <div class="fixed bottom-6 right-6">
269
+ <button id="fab" class="bg-blue-600 hover:bg-blue-700 text-white w-12 h-12 rounded-full shadow-lg flex items-center justify-center pulse">
270
+ <i class="fas fa-plus"></i>
271
+ </button>
272
+ </div>
273
+
274
+ <!-- Modals -->
275
+ <!-- Add Category Modal -->
276
+ <div id="add-category-modal" class="fixed inset-0 z-50 hidden items-center justify-center modal-backdrop">
277
+ <div class="bg-white rounded-lg shadow-xl max-w-md w-full mx-4 animate-fade-in">
278
+ <div class="p-6">
279
+ <h3 class="text-lg font-bold text-gray-800 mb-4">Add New Category</h3>
280
+ <form id="add-category-form">
281
+ <div class="mb-4">
282
+ <label for="category-name" class="block text-sm font-medium text-gray-700 mb-1">Category Name</label>
283
+ <input type="text" id="category-name" class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500">
284
+ </div>
285
+ <div class="mb-4">
286
+ <label for="category-icon" class="block text-sm font-medium text-gray-700 mb-1">Icon</label>
287
+ <select id="category-icon" class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500">
288
+ <option value="fa-brain">Brain</option>
289
+ <option value="fa-heartbeat">Health</option>
290
+ <option value="fa-briefcase">Career</option>
291
+ <option value="fa-heart">Relationships</option>
292
+ <option value="fa-gamepad">Hobbies</option>
293
+ <option value="fa-spa">Spiritual</option>
294
+ <option value="fa-home">Home</option>
295
+ <option value="fa-globe">Travel</option>
296
+ </select>
297
+ </div>
298
+ <div class="mb-4">
299
+ <label for="category-color" class="block text-sm font-medium text-gray-700 mb-1">Color Theme</label>
300
+ <select id="category-color" class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500">
301
+ <option value="purple">Purple</option>
302
+ <option value="blue">Blue</option>
303
+ <option value="red">Red</option>
304
+ <option value="green">Green</option>
305
+ <option value="yellow">Yellow</option>
306
+ <option value="pink">Pink</option>
307
+ <option value="indigo">Indigo</option>
308
+ <option value="teal">Teal</option>
309
+ </select>
310
+ </div>
311
+ <div class="flex justify-end space-x-3">
312
+ <button type="button" id="cancel-add-category" class="px-4 py-2 text-gray-600 rounded-md hover:bg-gray-100 transition">
313
+ Cancel
314
+ </button>
315
+ <button type="submit" class="px-4 py-2 bg-blue-600 text-white rounded-md hover:bg-blue-700 transition">
316
+ Add Category
317
+ </button>
318
+ </div>
319
+ </form>
320
+ </div>
321
+ </div>
322
+ </div>
323
+
324
+ <!-- Add Goal Modal -->
325
+ <div id="add-goal-modal" class="fixed inset-0 z-50 hidden items-center justify-center modal-backdrop">
326
+ <div class="bg-white rounded-lg shadow-xl max-w-md w-full mx-4 animate-fade-in">
327
+ <div class="p-6">
328
+ <h3 class="text-lg font-bold text-gray-800 mb-4">Add New Goal</h3>
329
+ <form id="add-goal-form">
330
+ <div class="mb-4">
331
+ <label for="goal-category" class="block text-sm font-medium text-gray-700 mb-1">Category</label>
332
+ <select id="goal-category" class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500">
333
+ <!-- Categories will be populated dynamically -->
334
+ </select>
335
+ </div>
336
+ <div class="mb-4">
337
+ <label for="goal-title" class="block text-sm font-medium text-gray-700 mb-1">Goal Title</label>
338
+ <input type="text" id="goal-title" class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500">
339
+ </div>
340
+ <div class="mb-4">
341
+ <label for="goal-description" class="block text-sm font-medium text-gray-700 mb-1">Description</label>
342
+ <textarea id="goal-description" class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500" rows="3"></textarea>
343
+ </div>
344
+ <div class="mb-4">
345
+ <label for="goal-due-date" class="block text-sm font-medium text-gray-700 mb-1">Due Date (optional)</label>
346
+ <input type="date" id="goal-due-date" class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500">
347
+ </div>
348
+ <div class="flex justify-end space-x-3">
349
+ <button type="button" id="cancel-add-goal" class="px-4 py-2 text-gray-600 rounded-md hover:bg-gray-100 transition">
350
+ Cancel
351
+ </button>
352
+ <button type="submit" class="px-4 py-2 bg-blue-600 text-white rounded-md hover:bg-blue-700 transition">
353
+ Add Goal
354
+ </button>
355
+ </div>
356
+ </form>
357
+ </div>
358
+ </div>
359
+ </div>
360
+
361
+ <!-- View Goals Modal -->
362
+ <div id="view-goals-modal" class="fixed inset-0 z-50 hidden items-center justify-center modal-backdrop">
363
+ <div class="bg-white rounded-lg shadow-xl max-w-2xl w-full mx-4 animate-fade-in max-h-[80vh] flex flex-col">
364
+ <div class="p-6 border-b border-gray-200">
365
+ <h3 class="text-lg font-bold text-gray-800">All Goals</h3>
366
+ </div>
367
+ <div id="goals-list-container" class="overflow-y-auto p-4 flex-1">
368
+ <!-- Goals will be listed here -->
369
+ </div>
370
+ <div class="p-4 border-t border-gray-200 flex justify-end">
371
+ <button id="close-view-goals" class="px-4 py-2 bg-blue-600 text-white rounded-md hover:bg-blue-700 transition">
372
+ Close
373
+ </button>
374
+ </div>
375
+ </div>
376
+ </div>
377
+
378
+ <!-- Profile Modal -->
379
+ <div id="profile-modal" class="fixed inset-0 z-50 hidden items-center justify-center modal-backdrop">
380
+ <div class="bg-white rounded-lg shadow-xl max-w-md w-full mx-4 animate-fade-in">
381
+ <div class="p-6">
382
+ <div class="flex items-center mb-6">
383
+ <div class="w-16 h-16 rounded-full bg-blue-100 flex items-center justify-center text-blue-600 text-2xl mr-4">
384
+ <i class="fas fa-user"></i>
385
+ </div>
386
+ <div>
387
+ <h3 class="text-lg font-bold text-gray-800" id="profile-username">User</h3>
388
+ <p class="text-gray-600 text-sm">Member since <span id="member-since">2023</span></p>
389
+ </div>
390
+ </div>
391
+ <div class="space-y-4 mb-6">
392
+ <div>
393
+ <h4 class="text-sm font-semibold text-gray-700 mb-1">Categories</h4>
394
+ <p class="text-gray-600" id="categories-count">0</p>
395
+ </div>
396
+ <div>
397
+ <h4 class="text-sm font-semibold text-gray-700 mb-1">Goals</h4>
398
+ <p class="text-gray-600" id="goals-count">0</p>
399
+ </div>
400
+ <div>
401
+ <h4 class="text-sm font-semibold text-gray-700 mb-1">Tasks Completed</h4>
402
+ <p class="text-gray-600" id="tasks-completed">0</p>
403
+ </div>
404
+ </div>
405
+ <div class="flex justify-end">
406
+ <button id="close-profile" class="px-4 py-2 bg-blue-600 text-white rounded-md hover:bg-blue-700 transition">
407
+ Close
408
+ </button>
409
+ </div>
410
+ </div>
411
+ </div>
412
+ </div>
413
+
414
+ <!-- Import/Export Modal -->
415
+ <div id="data-modal" class="fixed inset-0 z-50 hidden items-center justify-center modal-backdrop">
416
+ <div class="bg-white rounded-lg shadow-xl max-w-md w-full mx-4 animate-fade-in">
417
+ <div class="p-6">
418
+ <div class="flex border-b border-gray-200">
419
+ <button id="export-tab" class="px-4 py-2 font-medium text-blue-600 border-b-2 border-blue-600">Export Data</button>
420
+ <button id="import-tab" class="px-4 py-2 font-medium text-gray-500 hover:text-gray-700">Import Data</button>
421
+ </div>
422
+ <div id="export-content" class="py-4">
423
+ <p class="text-gray-600 text-sm mb-4">Export all your data as a JSON file for backup.</p>
424
+ <div class="mb-4">
425
+ <label class="block text-sm font-medium text-gray-700 mb-1">Your Data</label>
426
+ <textarea id="export-data" class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500" rows="5" readonly></textarea>
427
+ </div>
428
+ <button id="download-export" class="w-full px-4 py-2 bg-blue-600 text-white rounded-md hover:bg-blue-700 transition">
429
+ <i class="fas fa-download mr-2"></i> Download Backup
430
+ </button>
431
+ </div>
432
+ <div id="import-content" class="py-4 hidden">
433
+ <p class="text-gray-600 text-sm mb-4">Import your data from a previously exported JSON file. This will overwrite your current data!</p>
434
+ <div class="mb-4">
435
+ <label class="block text-sm font-medium text-gray-700 mb-1">Upload JSON File</label>
436
+ <input type="file" id="import-file" class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500">
437
+ </div>
438
+ <div class="flex space-x-3">
439
+ <button id="cancel-import" class="flex-1 px-4 py-2 text-gray-600 rounded-md hover:bg-gray-100 transition">
440
+ Cancel
441
+ </button>
442
+ <button id="confirm-import" class="flex-1 px-4 py-2 bg-blue-600 text-white rounded-md hover:bg-blue-700 transition">
443
+ Import Data
444
+ </button>
445
+ </div>
446
+ </div>
447
+ </div>
448
+ </div>
449
+ </div>
450
+
451
+ <!-- Confirmation Modal -->
452
+ <div id="confirm-modal" class="fixed inset-0 z-50 hidden items-center justify-center modal-backdrop">
453
+ <div class="bg-white rounded-lg shadow-xl max-w-md w-full mx-4 animate-fade-in">
454
+ <div class="p-6">
455
+ <h3 id="confirm-title" class="text-lg font-bold text-gray-800 mb-4">Confirm Action</h3>
456
+ <p id="confirm-message" class="text-gray-600 mb-6">Are you sure you want to perform this action?</p>
457
+ <div class="flex justify-end space-x-3">
458
+ <button id="confirm-cancel" class="px-4 py-2 text-gray-600 rounded-md hover:bg-gray-100 transition">
459
+ Cancel
460
+ </button>
461
+ <button id="confirm-action" class="px-4 py-2 bg-red-600 text-white rounded-md hover:bg-red-700 transition">
462
+ Confirm
463
+ </button>
464
+ </div>
465
+ </div>
466
+ </div>
467
+ </div>
468
+
469
+ <script>
470
+ // Data structure
471
+ let appData = {
472
+ categories: [],
473
+ goals: [],
474
+ completedTasks: [],
475
+ recentActivities: [],
476
+ settings: {
477
+ username: "Life Planner User",
478
+ createdAt: new Date().toISOString()
479
+ }
480
+ };
481
+
482
+ // Color themes
483
+ const colorThemes = {
484
+ purple: {
485
+ bgLight: 'bg-purple-100',
486
+ text: 'text-purple-800',
487
+ bg: 'bg-purple-600',
488
+ hover: 'hover:bg-purple-700',
489
+ border: 'border-purple-500'
490
+ },
491
+ blue: {
492
+ bgLight: 'bg-blue-100',
493
+ text: 'text-blue-800',
494
+ bg: 'bg-blue-600',
495
+ hover: 'hover:bg-blue-700',
496
+ border: 'border-blue-500'
497
+ },
498
+ red: {
499
+ bgLight: 'bg-red-100',
500
+ text: 'text-red-800',
501
+ bg: 'bg-red-600',
502
+ hover: 'hover:bg-red-700',
503
+ border: 'border-red-500'
504
+ },
505
+ green: {
506
+ bgLight: 'bg-green-100',
507
+ text: 'text-green-800',
508
+ bg: 'bg-green-600',
509
+ hover: 'hover:bg-green-700',
510
+ border: 'border-green-500'
511
+ },
512
+ yellow: {
513
+ bgLight: 'bg-yellow-100',
514
+ text: 'text-yellow-800',
515
+ bg: 'bg-yellow-600',
516
+ hover: 'hover:bg-yellow-700',
517
+ border: 'border-yellow-500'
518
+ },
519
+ pink: {
520
+ bgLight: 'bg-pink-100',
521
+ text: 'text-pink-800',
522
+ bg: 'bg-pink-600',
523
+ hover: 'hover:bg-pink-700',
524
+ border: 'border-pink-500'
525
+ },
526
+ indigo: {
527
+ bgLight: 'bg-indigo-100',
528
+ text: 'text-indigo-800',
529
+ bg: 'bg-indigo-600',
530
+ hover: 'hover:bg-indigo-700',
531
+ border: 'border-indigo-500'
532
+ },
533
+ teal: {
534
+ bgLight: 'bg-teal-100',
535
+ text: 'text-teal-800',
536
+ bg: 'bg-teal-600',
537
+ hover: 'hover:bg-teal-700',
538
+ border: 'border-teal-500'
539
+ }
540
+ };
541
+
542
+ // DOM Elements
543
+ const categoriesContainer = document.getElementById('categories-container');
544
+ const recentUpdatesList = document.getElementById('recent-updates');
545
+ const balancePercentage = document.getElementById('balance-percentage');
546
+ const balanceChart = document.getElementById('balance-chart');
547
+ const balanceLegend = document.getElementById('balance-legend');
548
+
549
+ // Modals
550
+ const addCategoryModal = document.getElementById('add-category-modal');
551
+ const addGoalModal = document.getElementById('add-goal-modal');
552
+ const viewGoalsModal = document.getElementById('view-goals-modal');
553
+ const profileModal = document.getElementById('profile-modal');
554
+ const dataModal = document.getElementById('data-modal');
555
+ const confirmModal = document.getElementById('confirm-modal');
556
+
557
+ // Buttons
558
+ const addCategoryBtn = document.getElementById('add-category-btn');
559
+ const viewAllBtn = document.getElementById('view-all-btn');
560
+ const quickAddGoal = document.getElementById('quick-add-goal');
561
+ const quickViewTasks = document.getElementById('quick-view-tasks');
562
+ const quickViewProgress = document.getElementById('quick-view-progress');
563
+ const quickViewCalendar = document.getElementById('quick-view-calendar');
564
+ const profileBtn = document.getElementById('profile-btn');
565
+ const exportBtn = document.getElementById('export-btn');
566
+ const importBtn = document.getElementById('import-btn');
567
+ const fab = document.getElementById('fab');
568
+ const resetDataBtn = document.getElementById('reset-data-btn');
569
+ const backupDataBtn = document.getElementById('backup-data-btn');
570
+
571
+ // Form elements
572
+ const addCategoryForm = document.getElementById('add-category-form');
573
+ const addGoalForm = document.getElementById('add-goal-form');
574
+ const goalCategorySelect = document.getElementById('goal-category');
575
+
576
+ // Data modal tabs
577
+ const exportTab = document.getElementById('export-tab');
578
+ const importTab = document.getElementById('import-tab');
579
+ const exportContent = document.getElementById('export-content');
580
+ const importContent = document.getElementById('import-content');
581
+ const exportDataTextarea = document.getElementById('export-data');
582
+ const importFileInput = document.getElementById('import-file');
583
+
584
+ // Confirmation modal elements
585
+ const confirmTitle = document.getElementById('confirm-title');
586
+ const confirmMessage = document.getElementById('confirm-message');
587
+ const confirmActionBtn = document.getElementById('confirm-action');
588
+
589
+ // Initialize the app
590
+ function initApp() {
591
+ loadData();
592
+ renderCategories();
593
+ renderRecentActivities();
594
+ updateBalanceChart();
595
+ updateProfileStats();
596
+
597
+ // Set initial animation for elements
598
+ document.querySelectorAll('.card').forEach((el, i) => {
599
+ setTimeout(() => {
600
+ el.style.opacity = '1';
601
+ el.style.transform = 'translateY(0)';
602
+ }, 100 * i);
603
+ });
604
+ }
605
+
606
+ // Load data from localStorage
607
+ function loadData() {
608
+ const savedData = localStorage.getItem('lifePlannerData');
609
+ if (savedData) {
610
+ appData = JSON.parse(savedData);
611
+ } else {
612
+ // Add some default categories if no data exists
613
+ appData.categories = [
614
+ {
615
+ id: 'cat1',
616
+ name: 'Personal Development',
617
+ icon: 'fa-brain',
618
+ color: 'purple',
619
+ createdAt: new Date().toISOString()
620
+ },
621
+ {
622
+ id: 'cat2',
623
+ name: 'Health & Fitness',
624
+ icon: 'fa-heartbeat',
625
+ color: 'red',
626
+ createdAt: new Date().toISOString()
627
+ },
628
+ {
629
+ id: 'cat3',
630
+ name: 'Career & Finance',
631
+ icon: 'fa-briefcase',
632
+ color: 'blue',
633
+ createdAt: new Date().toISOString()
634
+ }
635
+ ];
636
+
637
+ appData.goals = [
638
+ {
639
+ id: 'goal1',
640
+ categoryId: 'cat1',
641
+ title: 'Read 12 books this year',
642
+ description: 'Focus on personal growth and fiction books',
643
+ dueDate: new Date(new Date().getFullYear(), 11, 31).toISOString().split('T')[0],
644
+ completed: false,
645
+ createdAt: new Date().toISOString()
646
+ },
647
+ {
648
+ id: 'goal2',
649
+ categoryId: 'cat2',
650
+ title: 'Work out 3 times a week',
651
+ description: 'Mix of cardio and strength training',
652
+ dueDate: null,
653
+ completed: false,
654
+ createdAt: new Date().toISOString()
655
+ }
656
+ ];
657
+
658
+ saveData();
659
+ }
660
+ }
661
+
662
+ // Save data to localStorage
663
+ function saveData() {
664
+ localStorage.setItem('lifePlannerData', JSON.stringify(appData));
665
+ }
666
+
667
+ // Render categories
668
+ function renderCategories() {
669
+ categoriesContainer.innerHTML = '';
670
+
671
+ appData.categories.forEach(category => {
672
+ const goalsInCategory = appData.goals.filter(goal => goal.categoryId === category.id);
673
+ const completedGoals = goalsInCategory.filter(goal => goal.completed).length;
674
+ const progress = goalsInCategory.length > 0 ? Math.round((completedGoals / goalsInCategory.length) * 100) : 0;
675
+
676
+ const theme = colorThemes[category.color] || colorThemes.purple;
677
+
678
+ const card = document.createElement('div');
679
+ card.className = `card bg-white rounded-xl shadow-md overflow-hidden h-64 relative opacity-0 transform transition-all duration-300 ease-out`;
680
+ card.style.transform = 'translateY(10px)';
681
+
682
+ card.innerHTML = `
683
+ <div class="p-6 h-full flex flex-col">
684
+ <div class="flex justify-between items-start mb-4">
685
+ <div class="${theme.bgLight} ${theme.text} p-3 rounded-lg">
686
+ <i class="${category.icon} text-2xl"></i>
687
+ </div>
688
+ <span class="bg-${category.color}-100 text-${category.color}-800 text-xs px-2 py-1 rounded-full">
689
+ ${goalsInCategory.length} goals
690
+ </span>
691
+ </div>
692
+ <h3 class="text-xl font-bold text-gray-800 mb-2">${category.name}</h3>
693
+ <p class="text-gray-600 mb-4">${getCategoryDescription(category.name)}</p>
694
+ <div class="mt-auto">
695
+ <div class="flex justify-between items-center text-sm text-gray-500 mb-2">
696
+ <span>Progress</span>
697
+ <span>${progress}%</span>
698
+ </div>
699
+ <div class="w-full bg-gray-200 rounded-full h-2">
700
+ <div class="${theme.bg} h-2 rounded-full" style="width: ${progress}%"></div>
701
+ </div>
702
+ </div>
703
+ <button class="absolute bottom-4 right-4 ${theme.bg} text-white w-10 h-10 rounded-full flex items-center justify-center hover:${theme.hover} transition">
704
+ <i class="fas fa-arrow-right"></i>
705
+ </button>
706
+ </div>
707
+ `;
708
+
709
+ card.addEventListener('click', () => {
710
+ viewGoalsForCategory(category.id);
711
+ });
712
+
713
+ categoriesContainer.appendChild(card);
714
+
715
+ // Animate the card in
716
+ setTimeout(() => {
717
+ card.style.opacity = '1';
718
+ card.style.transform = 'translateY(0)';
719
+ }, 100);
720
+ });
721
+ }
722
+
723
+ // Get a description for a category based on its name
724
+ function getCategoryDescription(name) {
725
+ const descriptions = {
726
+ 'Personal Development': 'Grow your skills, knowledge, and mindset to become your best self.',
727
+ 'Health & Fitness': 'Track workouts, nutrition, sleep, and overall wellbeing.',
728
+ 'Career & Finance': 'Manage your professional growth and financial health.',
729
+ 'Relationships': 'Nurture your connections with family, friends, and partners.',
730
+ 'Recreation & Hobbies': 'Make time for fun, creativity, and personal interests.',
731
+ 'Spirituality & Purpose': 'Connect with your deeper values and life meaning.'
732
+ };
733
+
734
+ return descriptions[name] || 'Track and improve this important area of your life.';
735
+ }
736
+
737
+ // View goals for a specific category
738
+ function viewGoalsForCategory(categoryId) {
739
+ const category = appData.categories.find(c => c.id === categoryId);
740
+ if (!category) return;
741
+
742
+ const goals = appData.goals.filter(goal => goal.categoryId === categoryId);
743
+ const theme = colorThemes[category.color] || colorThemes.purple;
744
+
745
+ const goalsListContainer = document.getElementById('goals-list-container');
746
+ goalsListContainer.innerHTML = '';
747
+
748
+ // Add category header
749
+ const header = document.createElement('div');
750
+ header.className = 'flex items-center mb-6';
751
+ header.innerHTML = `
752
+ <div class="${theme.bgLight} ${theme.text} p-3 rounded-lg mr-4">
753
+ <i class="${category.icon} text-2xl"></i>
754
+ </div>
755
+ <h3 class="text-xl font-bold text-gray-800">${category.name} Goals</h3>
756
+ `;
757
+ goalsListContainer.appendChild(header);
758
+
759
+ if (goals.length === 0) {
760
+ const emptyMessage = document.createElement('p');
761
+ emptyMessage.className = 'text-gray-500 text-center py-8';
762
+ emptyMessage.textContent = 'No goals added yet. Click "Add Goal" to get started.';
763
+ goalsListContainer.appendChild(emptyMessage);
764
+ } else {
765
+ goals.forEach(goal => {
766
+ const goalCard = document.createElement('div');
767
+ goalCard.className = `mb-4 p-4 border rounded-lg ${goal.completed ? 'bg-gray-50 border-gray-200' : 'bg-white border-${category.color}-200'}`;
768
+
769
+ goalCard.innerHTML = `
770
+ <div class="flex justify-between">
771
+ <h4 class="font-medium ${goal.completed ? 'text-gray-500 line-through' : 'text-gray-800'}">${goal.title}</h4>
772
+ <div class="flex items-center space-x-2">
773
+ <button class="edit-goal text-gray-400 hover:text-${category.color}-600" data-id="${goal.id}">
774
+ <i class="fas fa-edit"></i>
775
+ </button>
776
+ <button class="toggle-goal text-${goal.completed ? 'gray-400' : category.color + '-600'}" data-id="${goal.id}">
777
+ <i class="fas fa-${goal.completed ? 'undo' : 'check'}"></i>
778
+ </button>
779
+ <button class="delete-goal text-gray-400 hover:text-red-600" data-id="${goal.id}">
780
+ <i class="fas fa-trash"></i>
781
+ </button>
782
+ </div>
783
+ </div>
784
+ ${goal.description ? `<p class="text-sm text-gray-600 mt-2">${goal.description}</p>` : ''}
785
+ ${goal.dueDate ? `<p class="text-xs mt-2 text-gray-500"><i class="fas fa-calendar-alt mr-1"></i> Due: ${formatDate(goal.dueDate)}</p>` : ''}
786
+ `;
787
+
788
+ goalsListContainer.appendChild(goalCard);
789
+ });
790
+ }
791
+
792
+ // Add "Add Goal" button at the bottom
793
+ const addGoalButton = document.createElement('button');
794
+ addGoalButton.className = `w-full mt-4 px-4 py-2 ${theme.bg} text-white rounded-lg hover:${theme.hover} transition flex items-center justify-center`;
795
+ addGoalButton.innerHTML = `<i class="fas fa-plus mr-2"></i> Add Goal`;
796
+ addGoalButton.addEventListener('click', () => {
797
+ showAddGoalModal(categoryId);
798
+ closeModal(viewGoalsModal);
799
+ });
800
+
801
+ goalsListContainer.appendChild(addGoalButton);
802
+
803
+ // Set up event listeners for the goal actions
804
+ document.querySelectorAll('.edit-goal').forEach(btn => {
805
+ btn.addEventListener('click', (e) => {
806
+ e.stopPropagation();
807
+ const goalId = btn.getAttribute('data-id');
808
+ editGoal(goalId);
809
+ });
810
+ });
811
+
812
+ document.querySelectorAll('.toggle-goal').forEach(btn => {
813
+ btn.addEventListener('click', (e) => {
814
+ e.stopPropagation();
815
+ const goalId = btn.getAttribute('data-id');
816
+ toggleGoalCompletion(goalId);
817
+ });
818
+ });
819
+
820
+ document.querySelectorAll('.delete-goal').forEach(btn => {
821
+ btn.addEventListener('click', (e) => {
822
+ e.stopPropagation();
823
+ const goalId = btn.getAttribute('data-id');
824
+ confirmDeleteGoal(goalId);
825
+ });
826
+ });
827
+
828
+ showModal(viewGoalsModal);
829
+ }
830
+
831
+ // Show all goals in the view goals modal
832
+ function showAllGoals() {
833
+ const goalsListContainer = document.getElementById('goals-list-container');
834
+ goalsListContainer.innerHTML = '';
835
+
836
+ if (appData.goals.length === 0) {
837
+ const emptyMessage = document.createElement('p');
838
+ emptyMessage.className = 'text-gray-500 text-center py-8';
839
+ emptyMessage.textContent = 'No goals added yet. Click "Add Goal" to get started.';
840
+ goalsListContainer.appendChild(emptyMessage);
841
+ } else {
842
+ // Group goals by category
843
+ const goalsByCategory = {};
844
+ appData.goals.forEach(goal => {
845
+ if (!goalsByCategory[goal.categoryId]) {
846
+ goalsByCategory[goal.categoryId] = [];
847
+ }
848
+ goalsByCategory[goal.categoryId].push(goal);
849
+ });
850
+
851
+ // Display goals for each category
852
+ Object.keys(goalsByCategory).forEach(categoryId => {
853
+ const category = appData.categories.find(c => c.id === categoryId);
854
+ if (!category) return;
855
+
856
+ const theme = colorThemes[category.color] || colorThemes.purple;
857
+
858
+ // Category header
859
+ const header = document.createElement('div');
860
+ header.className = 'flex items-center mb-4';
861
+ header.innerHTML = `
862
+ <div class="${theme.bgLight} ${theme.text} p-3 rounded-lg mr-4">
863
+ <i class="${category.icon} text-xl"></i>
864
+ </div>
865
+ <h3 class="text-lg font-bold text-gray-800">${category.name}</h3>
866
+ `;
867
+ goalsListContainer.appendChild(header);
868
+
869
+ // Goals list
870
+ goalsByCategory[categoryId].forEach(goal => {
871
+ const goalCard = document.createElement('div');
872
+ goalCard.className = `mb-3 p-3 border rounded-lg ${goal.completed ? 'bg-gray-50 border-gray-200' : 'bg-white border-${category.color}-200'}`;
873
+
874
+ goalCard.innerHTML = `
875
+ <div class="flex justify-between">
876
+ <h4 class="font-medium text-sm ${goal.completed ? 'text-gray-500 line-through' : 'text-gray-800'}">${goal.title}</h4>
877
+ <div class="flex items-center space-x-2">
878
+ <button class="toggle-goal text-${goal.completed ? 'gray-400' : category.color + '-600'}" data-id="${goal.id}">
879
+ <i class="fas fa-${goal.completed ? 'undo' : 'check'} text-sm"></i>
880
+ </button>
881
+ <button class="delete-goal text-gray-400 hover:text-red-600" data-id="${goal.id}">
882
+ <i class="fas fa-trash text-sm"></i>
883
+ </button>
884
+ </div>
885
+ </div>
886
+ ${goal.dueDate ? `<p class="text-xs mt-1 text-gray-500"><i class="fas fa-calendar-alt mr-1"></i> Due: ${formatDate(goal.dueDate)}</p>` : ''}
887
+ `;
888
+
889
+ goalCard.addEventListener('click', () => {
890
+ // For small cards, just toggle completion on click
891
+ toggleGoalCompletion(goal.id);
892
+ });
893
+
894
+ goalsListContainer.appendChild(goalCard);
895
+ });
896
+
897
+ // Add divider between categories
898
+ if (Object.keys(goalsByCategory).length > 1) {
899
+ const divider = document.createElement('div');
900
+ divider.className = 'border-t border-gray-200 my-4';
901
+ goalsListContainer.appendChild(divider);
902
+ }
903
+ });
904
+ }
905
+
906
+ // Add "Add Goal" button at the bottom
907
+ const addGoalButton = document.createElement('button');
908
+ addGoalButton.className = `w-full mt-4 px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition flex items-center justify-center`;
909
+ addGoalButton.innerHTML = `<i class="fas fa-plus mr-2"></i> Add Goal`;
910
+ addGoalButton.addEventListener('click', () => {
911
+ showAddGoalModal();
912
+ closeModal(viewGoalsModal);
913
+ });
914
+
915
+ goalsListContainer.appendChild(addGoalButton);
916
+
917
+ // Set up event listeners for the goal actions
918
+ document.querySelectorAll('.toggle-goal').forEach(btn => {
919
+ btn.addEventListener('click', (e) => {
920
+ e.stopPropagation();
921
+ const goalId = btn.getAttribute('data-id');
922
+ toggleGoalCompletion(goalId);
923
+ });
924
+ });
925
+
926
+ document.querySelectorAll('.delete-goal').forEach(btn => {
927
+ btn.addEventListener('click', (e) => {
928
+ e.stopPropagation();
929
+ const goalId = btn.getAttribute('data-id');
930
+ confirmDeleteGoal(goalId);
931
+ });
932
+ });
933
+
934
+ showModal(viewGoalsModal);
935
+ }
936
+
937
+ // Show modal to add a new category
938
+ function showAddCategoryModal() {
939
+ showModal(addCategoryModal);
940
+ }
941
+
942
+ // Show modal to add a new goal
943
+ function showAddGoalModal(preSelectedCategoryId = null) {
944
+ // Populate the category select
945
+ goalCategorySelect.innerHTML = '';
946
+ appData.categories.forEach(category => {
947
+ const option = document.createElement('option');
948
+ option.value = category.id;
949
+ option.textContent = category.name;
950
+ if (category.id === preSelectedCategoryId) {
951
+ option.selected = true;
952
+ }
953
+ goalCategorySelect.appendChild(option);
954
+ });
955
+
956
+ // Reset form
957
+ document.getElementById('goal-title').value = '';
958
+ document.getElementById('goal-description').value = '';
959
+ document.getElementById('goal-due-date').value = '';
960
+
961
+ showModal(addGoalModal);
962
+ }
963
+
964
+ // Add a new category
965
+ function addCategory(name, icon, color) {
966
+ const newCategory = {
967
+ id: 'cat' + Date.now(),
968
+ name,
969
+ icon,
970
+ color,
971
+ createdAt: new Date().toISOString()
972
+ };
973
+
974
+ appData.categories.push(newCategory);
975
+
976
+ // Add activity
977
+ addActivity(`Added new category: "${name}"`);
978
+
979
+ saveData();
980
+ renderCategories();
981
+ updateProfileStats();
982
+ closeModal(addCategoryModal);
983
+ }
984
+
985
+ // Add a new goal
986
+ function addGoal(categoryId, title, description, dueDate) {
987
+ const newGoal = {
988
+ id: 'goal' + Date.now(),
989
+ categoryId,
990
+ title,
991
+ description,
992
+ dueDate: dueDate || null,
993
+ completed: false,
994
+ createdAt: new Date().toISOString()
995
+ };
996
+
997
+ appData.goals.push(newGoal);
998
+
999
+ // Add activity
1000
+ const category = appData.categories.find(c => c.id === categoryId);
1001
+ addActivity(`Added new goal to "${category.name}": "${title}"`);
1002
+
1003
+ saveData();
1004
+ renderCategories();
1005
+ updateBalanceChart();
1006
+ updateProfileStats();
1007
+ closeModal(addGoalModal);
1008
+ }
1009
+
1010
+ // Edit an existing goal
1011
+ function editGoal(goalId) {
1012
+ const goal = appData.goals.find(g => g.id === goalId);
1013
+ if (!goal) return;
1014
+
1015
+ const category = appData.categories.find(c => c.id === goal.categoryId);
1016
+ if (!category) return;
1017
+
1018
+ // Populate the form
1019
+ showAddGoalModal(goal.categoryId);
1020
+ document.getElementById('goal-title').value = goal.title;
1021
+ document.getElementById('goal-description').value = goal.description || '';
1022
+ document.getElementById('goal-due-date').value = goal.dueDate || '';
1023
+
1024
+ // Change the form to edit mode
1025
+ const form = document.getElementById('add-goal-form');
1026
+ form.removeEventListener('submit', handleAddGoalSubmit);
1027
+ form.addEventListener('submit', (e) => {
1028
+ e.preventDefault();
1029
+ updateGoal(goalId);
1030
+ });
1031
+
1032
+ const submitBtn = form.querySelector('button[type="submit"]');
1033
+ submitBtn.textContent = 'Update Goal';
1034
+ }
1035
+
1036
+ // Update an existing goal
1037
+ function updateGoal(goalId) {
1038
+ const goal = appData.goals.find(g => g.id === goalId);
1039
+ if (!goal) return;
1040
+
1041
+ const oldTitle = goal.title;
1042
+
1043
+ goal.title = document.getElementById('goal-title').value;
1044
+ goal.description = document.getElementById('goal-description').value;
1045
+ goal.dueDate = document.getElementById('goal-due-date').value || null;
1046
+
1047
+ // Add activity if title changed
1048
+ if (goal.title !== oldTitle) {
1049
+ const category = appData.categories.find(c => c.id === goal.categoryId);
1050
+ addActivity(`Updated goal in "${category.name}": "${oldTitle}" → "${goal.title}"`);
1051
+ }
1052
+
1053
+ saveData();
1054
+ renderCategories();
1055
+ updateProfileStats();
1056
+ closeModal(addGoalModal);
1057
+
1058
+ // Reset the form back to add mode
1059
+ const form = document.getElementById('add-goal-form');
1060
+ form.removeEventListener('submit', updateGoal);
1061
+ form.addEventListener('submit', handleAddGoalSubmit);
1062
+
1063
+ const submitBtn = form.querySelector('button[type="submit"]');
1064
+ submitBtn.textContent = 'Add Goal';
1065
+
1066
+ // Refresh the goals view if it's open
1067
+ if (viewGoalsModal.classList.contains('hidden')) return;
1068
+ showAllGoals();
1069
+ }
1070
+
1071
+ // Toggle goal completion status
1072
+ function toggleGoalCompletion(goalId) {
1073
+ const goal = appData.goals.find(g => g.id === goalId);
1074
+ if (!goal) return;
1075
+
1076
+ goal.completed = !goal.completed;
1077
+
1078
+ // Add activity
1079
+ const category = appData.categories.find(c => c.id === goal.categoryId);
1080
+ addActivity(`${goal.completed ? 'Completed' : 'Marked incomplete'} goal in "${category.name}": "${goal.title}"`);
1081
+
1082
+ // Update completed tasks count
1083
+ if (goal.completed) {
1084
+ appData.completedTasks.push({
1085
+ goalId,
1086
+ completedAt: new Date().toISOString()
1087
+ });
1088
+ } else {
1089
+ appData.completedTasks = appData.completedTasks.filter(t => t.goalId !== goalId);
1090
+ }
1091
+
1092
+ saveData();
1093
+ renderCategories();
1094
+ updateBalanceChart();
1095
+ updateProfileStats();
1096
+
1097
+ // Refresh the goals view if it's open
1098
+ if (viewGoalsModal.classList.contains('hidden')) return;
1099
+ showAllGoals();
1100
+ }
1101
+
1102
+ // Confirm deletion of a goal
1103
+ function confirmDeleteGoal(goalId) {
1104
+ const goal = appData.goals.find(g => g.id === goalId);
1105
+ if (!goal) return;
1106
+
1107
+ confirmTitle.textContent = 'Delete Goal';
1108
+ confirmMessage.textContent = `Are you sure you want to delete the goal "${goal.title}"? This action cannot be undone.`;
1109
+
1110
+ confirmActionBtn.className = 'px-4 py-2 bg-red-600 text-white rounded-md hover:bg-red-700 transition';
1111
+ confirmActionBtn.textContent = 'Delete';
1112
+
1113
+ showModal(confirmModal);
1114
+
1115
+ confirmActionBtn.onclick = () => {
1116
+ deleteGoal(goalId);
1117
+ closeModal(confirmModal);
1118
+ };
1119
+ }
1120
+
1121
+ // Delete a goal
1122
+ function deleteGoal(goalId) {
1123
+ const goalIndex = appData.goals.findIndex(g => g.id === goalId);
1124
+ if (goalIndex === -1) return;
1125
+
1126
+ const [deletedGoal] = appData.goals.splice(goalIndex, 1);
1127
+
1128
+ // Remove from completed tasks
1129
+ appData.completedTasks = appData.completedTasks.filter(t => t.goalId !== goalId);
1130
+
1131
+ // Add activity
1132
+ const category = appData.categories.find(c => c.id === deletedGoal.categoryId);
1133
+ addActivity(`Deleted goal from "${category.name}": "${deletedGoal.title}"`);
1134
+
1135
+ saveData();
1136
+ renderCategories();
1137
+ updateBalanceChart();
1138
+ updateProfileStats();
1139
+
1140
+ // Refresh the goals view if it's open
1141
+ if (viewGoalsModal.classList.contains('hidden')) return;
1142
+ showAllGoals();
1143
+ }
1144
+
1145
+ // Confirm reset all data
1146
+ function confirmResetData() {
1147
+ confirmTitle.textContent = 'Reset All Data';
1148
+ confirmMessage.textContent = 'Are you sure you want to reset all data? This will delete all your categories, goals, and activities. This action cannot be undone.';
1149
+
1150
+ confirmActionBtn.className = 'px-4 py-2 bg-red-600 text-white rounded-md hover:bg-red-700 transition';
1151
+ confirmActionBtn.textContent = 'Reset Data';
1152
+
1153
+ showModal(confirmModal);
1154
+
1155
+ confirmActionBtn.onclick = () => {
1156
+ resetAllData();
1157
+ closeModal(confirmModal);
1158
+ };
1159
+ }
1160
+
1161
+ // Reset all app data
1162
+ function resetAllData() {
1163
+ appData = {
1164
+ categories: [],
1165
+ goals: [],
1166
+ completedTasks: [],
1167
+ recentActivities: [],
1168
+ settings: {
1169
+ username: "Life Planner User",
1170
+ createdAt: new Date().toISOString()
1171
+ }
1172
+ };
1173
+
1174
+ // Add activity
1175
+ addActivity('Reset all data');
1176
+
1177
+ saveData();
1178
+ renderCategories();
1179
+ renderRecentActivities();
1180
+ updateBalanceChart();
1181
+ updateProfileStats();
1182
+ }
1183
+
1184
+ // Add a recent activity
1185
+ function addActivity(text) {
1186
+ appData.recentActivities.unshift({
1187
+ text,
1188
+ date: new Date().toISOString()
1189
+ });
1190
+
1191
+ // Keep only the last 20 activities
1192
+ if (appData.recentActivities.length > 20) {
1193
+ appData.recentActivities.pop();
1194
+ }
1195
+
1196
+ saveData();
1197
+ renderRecentActivities();
1198
+ }
1199
+
1200
+ // Render recent activities
1201
+ function renderRecentActivities() {
1202
+ recentUpdatesList.innerHTML = '';
1203
+
1204
+ if (appData.recentActivities.length === 0) {
1205
+ const emptyMessage = document.createElement('p');
1206
+ emptyMessage.className = 'text-gray-500 text-sm';
1207
+ emptyMessage.textContent = 'No recent activities yet.';
1208
+ recentUpdatesList.appendChild(emptyMessage);
1209
+ return;
1210
+ }
1211
+
1212
+ appData.recentActivities.forEach(activity => {
1213
+ const li = document.createElement('li');
1214
+ li.className = 'flex items-start';
1215
+
1216
+ // Assign a random icon based on activity text
1217
+ let icon = 'fa-bell';
1218
+ let iconColor = 'text-gray-500';
1219
+
1220
+ if (activity.text.includes('Added')) {
1221
+ icon = 'fa-plus-circle';
1222
+ iconColor = 'text-green-500';
1223
+ } else if (activity.text.includes('Updated')) {
1224
+ icon = 'fa-edit';
1225
+ iconColor = 'text-blue-500';
1226
+ } else if (activity.text.includes('Completed')) {
1227
+ icon = 'fa-check-circle';
1228
+ iconColor = 'text-purple-500';
1229
+ } else if (activity.text.includes('Deleted')) {
1230
+ icon = 'fa-trash-alt';
1231
+ iconColor = 'text-red-500';
1232
+ } else if (activity.text.includes('Reset')) {
1233
+ icon = 'fa-exclamation-triangle';
1234
+ iconColor = 'text-red-500';
1235
+ }
1236
+
1237
+ li.innerHTML = `
1238
+ <div class="${iconColor} p-1 rounded-lg mr-3 mt-1">
1239
+ <i class="fas ${icon}"></i>
1240
+ </div>
1241
+ <div>
1242
+ <p class="text-xs font-medium">${activity.text}</p>
1243
+ <p class="text-xs text-gray-500">${formatTimeAgo(activity.date)}</p>
1244
+ </div>
1245
+ `;
1246
+
1247
+ recentUpdatesList.appendChild(li);
1248
+ });
1249
+ }
1250
+
1251
+ // Update the balance chart
1252
+ function updateBalanceChart() {
1253
+ if (appData.categories.length === 0) {
1254
+ balancePercentage.textContent = '0%';
1255
+ balanceChart.innerHTML = '';
1256
+ balanceLegend.innerHTML = '';
1257
+ return;
1258
+ }
1259
+
1260
+ // Calculate progress for each category
1261
+ const categoryProgress = appData.categories.map(category => {
1262
+ const goalsInCategory = appData.goals.filter(goal => goal.categoryId === category.id);
1263
+ const completedGoals = goalsInCategory.filter(goal => goal.completed).length;
1264
+ const progress = goalsInCategory.length > 0 ? (completedGoals / goalsInCategory.length) * 100 : 0;
1265
+
1266
+ return {
1267
+ name: category.name,
1268
+ progress,
1269
+ color: category.color,
1270
+ icon: category.icon
1271
+ };
1272
+ });
1273
+
1274
+ // Calculate overall balance (average progress)
1275
+ const totalProgress = categoryProgress.reduce((sum, cat) => sum + cat.progress, 0);
1276
+ const averageProgress = totalProgress / categoryProgress.length;
1277
+ balancePercentage.textContent = `${Math.round(averageProgress)}%`;
1278
+
1279
+ // Update the pie chart (simplified version)
1280
+ balanceChart.innerHTML = '';
1281
+ const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
1282
+ svg.setAttribute("viewBox", "0 0 100 100");
1283
+ svg.classList.add('w-full', 'h-full');
1284
+
1285
+ let cumulativePercent = 0;
1286
+ categoryProgress.forEach((cat, index) => {
1287
+ const percent = (cat.progress / totalProgress) * 100;
1288
+ if (percent <= 0) return;
1289
+
1290
+ const startAngle = cumulativePercent * 3.6;
1291
+ cumulativePercent += percent;
1292
+ const endAngle = cumulativePercent * 3.6;
1293
+
1294
+ const start = polarToCartesian(50, 50, 45, startAngle);
1295
+ const end = polarToCartesian(50, 50, 45, endAngle);
1296
+ const largeArcFlag = endAngle - startAngle <= 180 ? 0 : 1;
1297
+
1298
+ const path = document.createElementNS("http://www.w3.org/2000/svg", "path");
1299
+ path.setAttribute("d", `M 50 50 L ${start.x} ${start.y} A 45 45 0 ${largeArcFlag} 1 ${end.x} ${end.y} Z`);
1300
+ path.setAttribute("fill", getComputedColor(cat.color));
1301
+ path.setAttribute("stroke", "#fff");
1302
+ path.setAttribute("stroke-width", "1");
1303
+
1304
+ svg.appendChild(path);
1305
+ });
1306
+
1307
+ balanceChart.appendChild(svg);
1308
+
1309
+ // Update the legend
1310
+ balanceLegend.innerHTML = '';
1311
+ categoryProgress.forEach(cat => {
1312
+ const legendItem = document.createElement('div');
1313
+ legendItem.className = 'flex items-center text-xs';
1314
+
1315
+ const colorSwatch = document.createElement('div');
1316
+ colorSwatch.className = `w-3 h-3 rounded-full mr-2 bg-${cat.color}-500`;
1317
+
1318
+ const progressText = document.createElement('span');
1319
+ progressText.className = 'font-medium';
1320
+ progressText.textContent = `${Math.round(cat.progress)}%`;
1321
+
1322
+ legendItem.appendChild(colorSwatch);
1323
+ legendItem.appendChild(document.createTextNode(cat.name + ' '));
1324
+ legendItem.appendChild(progressText);
1325
+
1326
+ balanceLegend.appendChild(legendItem);
1327
+ });
1328
+ }
1329
+
1330
+ // Helper function for pie chart
1331
+ function polarToCartesian(centerX, centerY, radius, angleInDegrees) {
1332
+ const angleInRadians = (angleInDegrees - 90) * Math.PI / 180.0;
1333
+
1334
+ return {
1335
+ x: centerX + (radius * Math.cos(angleInRadians)),
1336
+ y: centerY + (radius * Math.sin(angleInRadians))
1337
+ };
1338
+ }
1339
+
1340
+ // Helper function to get computed color for SVG
1341
+ function getComputedColor(colorName) {
1342
+ const colors = {
1343
+ purple: '#8b5cf6',
1344
+ blue: '#3b82f6',
1345
+ red: '#ef4444',
1346
+ green: '#10b981',
1347
+ yellow: '#f59e0b',
1348
+ pink: '#ec4899',
1349
+ indigo: '#6366f1',
1350
+ teal: '#14b8a6'
1351
+ };
1352
+
1353
+ return colors[colorName] || '#8b5cf6';
1354
+ }
1355
+
1356
+ // Update profile stats
1357
+ function updateProfileStats() {
1358
+ document.getElementById('profile-username').textContent = appData.settings.username;
1359
+ document.getElementById('member-since').textContent = new Date(appData.settings.createdAt).getFullYear();
1360
+ document.getElementById('categories-count').textContent = appData.categories.length;
1361
+ document.getElementById('goals-count').textContent = appData.goals.length;
1362
+ document.getElementById('tasks-completed').textContent = appData.completedTasks.length;
1363
+ }
1364
+
1365
+ // Export data
1366
+ function exportData() {
1367
+ const dataStr = JSON.stringify(appData, null, 2);
1368
+ exportDataTextarea.value = dataStr;
1369
+
1370
+ // Show export tab by default
1371
+ exportTab.click();
1372
+
1373
+ showModal(dataModal);
1374
+ }
1375
+
1376
+ // Download exported data
1377
+ function downloadExport() {
1378
+ const dataStr = "data:text/json;charset=utf-8," + encodeURIComponent(JSON.stringify(appData, null, 2));
1379
+ const downloadAnchorNode = document.createElement('a');
1380
+ downloadAnchorNode.setAttribute("href", dataStr);
1381
+ downloadAnchorNode.setAttribute("download", "life-planner-backup.json");
1382
+ document.body.appendChild(downloadAnchorNode);
1383
+ downloadAnchorNode.click();
1384
+ downloadAnchorNode.remove();
1385
+
1386
+ addActivity('Exported all data');
1387
+ }
1388
+
1389
+ // Import data
1390
+ function importData(file) {
1391
+ const reader = new FileReader();
1392
+ reader.onload = function(e) {
1393
+ try {
1394
+ const importedData = JSON.parse(e.target.result);
1395
+
1396
+ // Basic validation
1397
+ if (!importedData.categories || !importedData.goals || !importedData.settings) {
1398
+ alert("Invalid data file. Please select a valid backup file.");
1399
+ return;
1400
+ }
1401
+
1402
+ // Show confirmation
1403
+ confirmTitle.textContent = 'Import Data';
1404
+ confirmMessage.textContent = 'This will overwrite all your current data. Are you sure you want to continue?';
1405
+
1406
+ confirmActionBtn.className = 'px-4 py-2 bg-blue-600 text-white rounded-md hover:bg-blue-700 transition';
1407
+ confirmActionBtn.textContent = 'Import';
1408
+
1409
+ showModal(confirmModal);
1410
+
1411
+ confirmActionBtn.onclick = () => {
1412
+ appData = importedData;
1413
+ saveData();
1414
+
1415
+ addActivity('Imported all data from backup');
1416
+
1417
+ initApp(); // Refresh the UI
1418
+ closeModal(confirmModal);
1419
+ closeModal(dataModal);
1420
+ };
1421
+ } catch (err) {
1422
+ alert("Error reading the file. Please make sure it's a valid backup file.");
1423
+ console.error(err);
1424
+ }
1425
+ };
1426
+ reader.readAsText(file);
1427
+ }
1428
+
1429
+ // Format date for display
1430
+ function formatDate(dateStr) {
1431
+ if (!dateStr) return '';
1432
+ const date = new Date(dateStr);
1433
+ return date.toLocaleDateString('en-US', { year: 'numeric', month: 'short', day: 'numeric' });
1434
+ }
1435
+
1436
+ // Format time ago for activities
1437
+ function formatTimeAgo(dateStr) {
1438
+ const date = new Date(dateStr);
1439
+ const now = new Date();
1440
+ const diffInSeconds = Math.floor((now - date) / 1000);
1441
+
1442
+ if (diffInSeconds < 60) return 'Just now';
1443
+ if (diffInSeconds < 3600) return `${Math.floor(diffInSeconds / 60)} minutes ago`;
1444
+ if (diffInSeconds < 86400) return `${Math.floor(diffInSeconds / 3600)} hours ago`;
1445
+ if (diffInSeconds < 604800) return `${Math.floor(diffInSeconds / 86400)} days ago`;
1446
+
1447
+ return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric' });
1448
+ }
1449
+
1450
+ // Show modal
1451
+ function showModal(modal) {
1452
+ modal.classList.remove('hidden');
1453
+ modal.classList.add('flex');
1454
+
1455
+ // Prevent body scroll
1456
+ document.body.style.overflow = 'hidden';
1457
+ }
1458
+
1459
+ // Close modal
1460
+ function closeModal(modal) {
1461
+ modal.classList.add('hidden');
1462
+ modal.classList.remove('flex');
1463
+
1464
+ // Restore body scroll
1465
+ document.body.style.overflow = '';
1466
+ }
1467
+
1468
+ // Event listeners
1469
+
1470
+ // Form submission handlers
1471
+ function handleAddCategorySubmit(e) {
1472
+ e.preventDefault();
1473
+ const name = document.getElementById('category-name').value.trim();
1474
+ const icon = document.getElementById('category-icon').value;
1475
+ const color = document.getElementById('category-color').value;
1476
+
1477
+ if (!name) {
1478
+ alert('Please enter a category name');
1479
+ return;
1480
+ }
1481
+
1482
+ addCategory(name, icon, color);
1483
+ }
1484
+
1485
+ function handleAddGoalSubmit(e) {
1486
+ e.preventDefault();
1487
+ const categoryId = document.getElementById('goal-category').value;
1488
+ const title = document.getElementById('goal-title').value.trim();
1489
+ const description = document.getElementById('goal-description').value.trim();
1490
+ const dueDate = document.getElementById('goal-due-date').value;
1491
+
1492
+ if (!title) {
1493
+ alert('Please enter a goal title');
1494
+ return;
1495
+ }
1496
+
1497
+ addGoal(categoryId, title, description, dueDate);
1498
+ }
1499
+
1500
+ // Tab switching in data modal
1501
+ exportTab.addEventListener('click', () => {
1502
+ exportTab.classList.add('text-blue-600', 'border-blue-600');
1503
+ exportTab.classList.remove('text-gray-500');
1504
+
1505
+ importTab.classList.remove('text-blue-600', 'border-blue-600');
1506
+ importTab.classList.add('text-gray-500');
1507
+
1508
+ exportContent.classList.remove('hidden');
1509
+ importContent.classList.add('hidden');
1510
+
1511
+ // Populate export data
1512
+ exportDataTextarea.value = JSON.stringify(appData, null, 2);
1513
+ });
1514
+
1515
+ importTab.addEventListener('click', () => {
1516
+ importTab.classList.add('text-blue-600', 'border-blue-600');
1517
+ importTab.classList.remove('text-gray-500');
1518
+
1519
+ exportTab.classList.remove('text-blue-600', 'border-blue-600');
1520
+ exportTab.classList.add('text-gray-500');
1521
+
1522
+ importContent.classList.remove('hidden');
1523
+ exportContent.classList.add('hidden');
1524
+ });
1525
+
1526
+ // Button click handlers
1527
+ addCategoryBtn.addEventListener('click', showAddCategoryModal);
1528
+ viewAllBtn.addEventListener('click', showAllGoals);
1529
+ quickAddGoal.addEventListener('click', showAddGoalModal);
1530
+ quickViewTasks.addEventListener('click', () => alert('Feature coming soon!'));
1531
+ quickViewProgress.addEventListener('click', () => alert('Feature coming soon!'));
1532
+ quickViewCalendar.addEventListener('click', () => alert('Feature coming soon!'));
1533
+ profileBtn.addEventListener('click', () => {
1534
+ updateProfileStats();
1535
+ showModal(profileModal);
1536
+ });
1537
+ exportBtn.addEventListener('click', exportData);
1538
+ importBtn.addEventListener('click', exportData); // Same as export for now
1539
+ fab.addEventListener('click', showAddGoalModal);
1540
+ resetDataBtn.addEventListener('click', confirmResetData);
1541
+ backupDataBtn.addEventListener('click', exportData);
1542
+
1543
+ // Modal close handlers
1544
+ document.getElementById('cancel-add-category').addEventListener('click', () => closeModal(addCategoryModal));
1545
+ document.getElementById('cancel-add-goal').addEventListener('click', () => closeModal(addGoalModal));
1546
+ document.getElementById('close-view-goals').addEventListener('click', () => closeModal(viewGoalsModal));
1547
+ document.getElementById('close-profile').addEventListener('click', () => closeModal(profileModal));
1548
+ document.getElementById('cancel-import').addEventListener('click', () => closeModal(dataModal));
1549
+ document.getElementById('confirm-cancel').addEventListener('click', () => closeModal(confirmModal));
1550
+
1551
+ // Form submissions
1552
+ addCategoryForm.addEventListener('submit', handleAddCategorySubmit);
1553
+ addGoalForm.addEventListener('submit', handleAddGoalSubmit);
1554
+
1555
+ // Data modal actions
1556
+ document.getElementById('download-export').addEventListener('click', downloadExport);
1557
+ document.getElementById('confirm-import').addEventListener('click', () => {
1558
+ if (importFileInput.files.length > 0) {
1559
+ importData(importFileInput.files[0]);
1560
+ } else {
1561
+ alert('Please select a file to import');
1562
+ }
1563
+ });
1564
+
1565
+ // Close modals when clicking on backdrop
1566
+ [addCategoryModal, addGoalModal, viewGoalsModal, profileModal, dataModal, confirmModal].forEach(modal => {
1567
+ modal.addEventListener('click', (e) => {
1568
+ if (e.target === modal) {
1569
+ closeModal(modal);
1570
+ }
1571
+ });
1572
+ });
1573
+
1574
+ // Search functionality
1575
+ document.getElementById('category-search').addEventListener('input', (e) => {
1576
+ const searchTerm = e.target.value.toLowerCase();
1577
+ const cards = document.querySelectorAll('.card');
1578
+
1579
+ cards.forEach(card => {
1580
+ const title = card.querySelector('h3').textContent.toLowerCase();
1581
+ if (title.includes(searchTerm)) {
1582
+ card.style.display = 'block';
1583
+ } else {
1584
+ card.style.display = 'none';
1585
+ }
1586
+ });
1587
+ });
1588
+
1589
+ // Initialize the app
1590
+ document.addEventListener('DOMContentLoaded', initApp);
1591
+ </script>
1592
+ <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=Digiator/life-planner" style="color: #fff;text-decoration: underline;" target="_blank" >🧬 Remix</a></p></body>
1593
+ </html>