fgarola commited on
Commit
ce0962e
·
verified ·
1 Parent(s): 37a17d5

mejoralo muchisimo mas - Initial Deployment

Browse files
Files changed (2) hide show
  1. README.md +7 -5
  2. index.html +553 -19
README.md CHANGED
@@ -1,10 +1,12 @@
1
  ---
2
- title: Gartime
3
- emoji: 🐨
4
- colorFrom: purple
5
- colorTo: blue
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: gartime
3
+ emoji: 🐳
4
+ colorFrom: green
5
+ colorTo: gray
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,553 @@
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="es">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>GarTime - Gestión Profesional de Tareas</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
+ .timer-active {
11
+ animation: pulse 2s infinite;
12
+ }
13
+ @keyframes pulse {
14
+ 0% { box-shadow: 0 0 0 0 rgba(59, 130, 246, 0.7); }
15
+ 70% { box-shadow: 0 0 0 10px rgba(59, 130, 246, 0); }
16
+ 100% { box-shadow: 0 0 0 0 rgba(59, 130, 246, 0); }
17
+ }
18
+ .task-completed {
19
+ background-color: #f0fdf4;
20
+ border-left: 4px solid #10b981;
21
+ }
22
+ .dark .task-completed {
23
+ background-color: #052e16;
24
+ border-left: 4px solid #10b981;
25
+ }
26
+ .dark-mode {
27
+ background-color: #1f2937;
28
+ color: #f3f4f6;
29
+ }
30
+ .dark-mode-secondary {
31
+ background-color: #111827;
32
+ }
33
+ </style>
34
+ </head>
35
+ <body class="bg-gray-50 min-h-screen transition-colors duration-300">
36
+ <div class="container mx-auto px-4 py-8 max-w-6xl">
37
+ <!-- Header -->
38
+ <header class="flex flex-col md:flex-row justify-between items-center mb-8 gap-4">
39
+ <div class="flex items-center">
40
+ <div class="bg-blue-600 text-white p-3 rounded-lg mr-4">
41
+ <i class="fas fa-clock text-2xl"></i>
42
+ </div>
43
+ <div>
44
+ <h1 class="text-3xl font-bold text-gray-800 dark:text-white">GarTime</h1>
45
+ <p class="text-gray-600 dark:text-gray-300">Gestión profesional de tareas y tiempo</p>
46
+ </div>
47
+ </div>
48
+
49
+ <div class="flex items-center space-x-4">
50
+ <div class="flex items-center">
51
+ <span class="mr-2 text-gray-700 dark:text-gray-300">Modo oscuro</span>
52
+ <button id="darkModeToggle" class="relative inline-flex h-6 w-11 items-center rounded-full bg-gray-200 dark:bg-gray-600">
53
+ <span class="sr-only">Toggle dark mode</span>
54
+ <span id="darkModeToggleCircle" class="inline-block h-4 w-4 transform rounded-full bg-white dark:bg-gray-300 translate-x-1 dark:translate-x-6 transition-transform"></span>
55
+ </button>
56
+ </div>
57
+
58
+ <div class="hidden md:block">
59
+ <div class="flex items-center bg-blue-50 dark:bg-blue-900/30 p-2 rounded-lg">
60
+ <i class="fas fa-dollar-sign text-blue-600 dark:text-blue-300 mr-2"></i>
61
+ <span class="font-medium text-blue-600 dark:text-blue-300">Tarifa:</span>
62
+ <input id="hourlyRate" type="number" min="0" step="0.01" value="50.00" class="ml-2 w-20 bg-transparent border-b border-blue-300 dark:border-blue-600 focus:outline-none text-blue-800 dark:text-blue-100">
63
+ <span class="ml-1 text-blue-600 dark:text-blue-300">/h</span>
64
+ </div>
65
+ </div>
66
+ </div>
67
+ </header>
68
+
69
+ <!-- Stats Cards -->
70
+ <div class="grid grid-cols-1 md:grid-cols-3 gap-4 mb-8">
71
+ <div class="bg-white dark:bg-gray-800 rounded-lg shadow p-6 flex items-center">
72
+ <div class="bg-green-100 dark:bg-green-900/30 p-3 rounded-full mr-4">
73
+ <i class="fas fa-check-circle text-green-600 dark:text-green-400 text-xl"></i>
74
+ </div>
75
+ <div>
76
+ <p class="text-gray-500 dark:text-gray-400 text-sm">Tareas completadas</p>
77
+ <h3 id="completedTasksCount" class="text-2xl font-bold text-gray-800 dark:text-white">0</h3>
78
+ </div>
79
+ </div>
80
+
81
+ <div class="bg-white dark:bg-gray-800 rounded-lg shadow p-6 flex items-center">
82
+ <div class="bg-blue-100 dark:bg-blue-900/30 p-3 rounded-full mr-4">
83
+ <i class="fas fa-clock text-blue-600 dark:text-blue-400 text-xl"></i>
84
+ </div>
85
+ <div>
86
+ <p class="text-gray-500 dark:text-gray-400 text-sm">Tiempo total</p>
87
+ <h3 id="totalTime" class="text-2xl font-bold text-gray-800 dark:text-white">00:00:00</h3>
88
+ </div>
89
+ </div>
90
+
91
+ <div class="bg-white dark:bg-gray-800 rounded-lg shadow p-6 flex items-center">
92
+ <div class="bg-purple-100 dark:bg-purple-900/30 p-3 rounded-full mr-4">
93
+ <i class="fas fa-money-bill-wave text-purple-600 dark:text-purple-400 text-xl"></i>
94
+ </div>
95
+ <div>
96
+ <p class="text-gray-500 dark:text-gray-400 text-sm">Valor total</p>
97
+ <h3 id="totalValue" class="text-2xl font-bold text-gray-800 dark:text-white">$0.00</h3>
98
+ </div>
99
+ </div>
100
+ </div>
101
+
102
+ <!-- Main Content -->
103
+ <div class="flex flex-col lg:flex-row gap-8">
104
+ <!-- Task Form -->
105
+ <div class="lg:w-1/3">
106
+ <div class="bg-white dark:bg-gray-800 rounded-lg shadow p-6 sticky top-4">
107
+ <h2 class="text-xl font-bold text-gray-800 dark:text-white mb-4">Nueva Tarea</h2>
108
+
109
+ <div class="mb-4">
110
+ <label for="taskName" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Nombre de la tarea</label>
111
+ <input type="text" id="taskName" class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 dark:bg-gray-700 dark:text-white" placeholder="Ej: Diseño de logo">
112
+ </div>
113
+
114
+ <div class="mb-4">
115
+ <label for="taskDescription" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Descripción</label>
116
+ <textarea id="taskDescription" rows="3" class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 dark:bg-gray-700 dark:text-white" placeholder="Detalles de la tarea..."></textarea>
117
+ </div>
118
+
119
+ <div class="mb-6">
120
+ <label for="taskProject" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Proyecto</label>
121
+ <input type="text" id="taskProject" class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 dark:bg-gray-700 dark:text-white" placeholder="Ej: Sitio web cliente X">
122
+ </div>
123
+
124
+ <div class="md:hidden mb-6">
125
+ <label for="mobileHourlyRate" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Tarifa por hora</label>
126
+ <div class="relative">
127
+ <div class="absolute inset-y-0 left-0 flex items-center pl-3 pointer-events-none">
128
+ <span class="text-gray-500 dark:text-gray-400">$</span>
129
+ </div>
130
+ <input id="mobileHourlyRate" type="number" min="0" step="0.01" value="50.00" class="pl-8 w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 dark:bg-gray-700 dark:text-white">
131
+ </div>
132
+ </div>
133
+
134
+ <button id="addTaskBtn" class="w-full bg-blue-600 hover:bg-blue-700 text-white font-medium py-2 px-4 rounded-md transition duration-300 flex items-center justify-center">
135
+ <i class="fas fa-plus mr-2"></i> Agregar Tarea
136
+ </button>
137
+ </div>
138
+ </div>
139
+
140
+ <!-- Task List -->
141
+ <div class="lg:w-2/3">
142
+ <div class="bg-white dark:bg-gray-800 rounded-lg shadow overflow-hidden">
143
+ <div class="border-b border-gray-200 dark:border-gray-700 px-6 py-4 flex justify-between items-center">
144
+ <h2 class="text-xl font-bold text-gray-800 dark:text-white">Mis Tareas</h2>
145
+ <div class="relative">
146
+ <select id="taskFilter" class="appearance-none bg-gray-100 dark:bg-gray-700 border border-gray-300 dark:border-gray-600 text-gray-700 dark:text-white py-2 px-4 pr-8 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500">
147
+ <option value="all">Todas</option>
148
+ <option value="active">Activas</option>
149
+ <option value="completed">Completadas</option>
150
+ </select>
151
+ <div class="pointer-events-none absolute inset-y-0 right-0 flex items-center px-2 text-gray-700 dark:text-gray-300">
152
+ <i class="fas fa-chevron-down"></i>
153
+ </div>
154
+ </div>
155
+ </div>
156
+
157
+ <div id="taskList" class="divide-y divide-gray-200 dark:divide-gray-700">
158
+ <!-- Tasks will be added here dynamically -->
159
+ <div class="p-6 text-center text-gray-500 dark:text-gray-400">
160
+ <i class="fas fa-tasks text-4xl mb-2"></i>
161
+ <p>No hay tareas registradas</p>
162
+ <p class="text-sm">Agrega tu primera tarea usando el formulario</p>
163
+ </div>
164
+ </div>
165
+ </div>
166
+ </div>
167
+ </div>
168
+ </div>
169
+
170
+ <!-- Task Template (hidden) -->
171
+ <template id="taskTemplate">
172
+ <div class="task-item p-6 hover:bg-gray-50 dark:hover:bg-gray-700/50 transition-colors duration-200">
173
+ <div class="flex justify-between items-start mb-2">
174
+ <div>
175
+ <h3 class="task-name font-medium text-lg text-gray-800 dark:text-white"></h3>
176
+ <p class="task-project text-sm text-blue-600 dark:text-blue-400 mt-1"></p>
177
+ </div>
178
+ <div class="flex space-x-2">
179
+ <button class="timer-toggle-btn bg-blue-100 dark:bg-blue-900/30 hover:bg-blue-200 dark:hover:bg-blue-800 text-blue-800 dark:text-blue-200 px-3 py-1 rounded-md text-sm flex items-center transition-colors duration-200">
180
+ <i class="fas fa-play mr-1"></i> <span class="timer-label">Iniciar</span>
181
+ </button>
182
+ <button class="complete-btn bg-green-100 dark:bg-green-900/30 hover:bg-green-200 dark:hover:bg-green-800 text-green-800 dark:text-green-200 px-3 py-1 rounded-md text-sm flex items-center transition-colors duration-200">
183
+ <i class="fas fa-check mr-1"></i> Completar
184
+ </button>
185
+ <button class="delete-btn bg-red-100 dark:bg-red-900/30 hover:bg-red-200 dark:hover:bg-red-800 text-red-800 dark:text-red-200 px-3 py-1 rounded-md text-sm flex items-center transition-colors duration-200">
186
+ <i class="fas fa-trash mr-1"></i>
187
+ </button>
188
+ </div>
189
+ </div>
190
+
191
+ <p class="task-description text-gray-600 dark:text-gray-300 mb-3"></p>
192
+
193
+ <div class="flex flex-wrap justify-between items-center pt-3 border-t border-gray-200 dark:border-gray-700">
194
+ <div class="flex items-center text-sm text-gray-500 dark:text-gray-400">
195
+ <i class="fas fa-clock mr-1"></i>
196
+ <span class="task-time">00:00:00</span>
197
+ </div>
198
+ <div class="flex items-center text-sm font-medium">
199
+ <span class="text-gray-500 dark:text-gray-400 mr-1">Valor:</span>
200
+ <span class="task-value text-blue-600 dark:text-blue-400">$0.00</span>
201
+ </div>
202
+ <div class="text-xs text-gray-400 task-date"></div>
203
+ </div>
204
+ </div>
205
+ </template>
206
+
207
+ <script>
208
+ document.addEventListener('DOMContentLoaded', function() {
209
+ // DOM Elements
210
+ const darkModeToggle = document.getElementById('darkModeToggle');
211
+ const darkModeToggleCircle = document.getElementById('darkModeToggleCircle');
212
+ const hourlyRateInput = document.getElementById('hourlyRate');
213
+ const mobileHourlyRateInput = document.getElementById('mobileHourlyRate');
214
+ const taskNameInput = document.getElementById('taskName');
215
+ const taskDescriptionInput = document.getElementById('taskDescription');
216
+ const taskProjectInput = document.getElementById('taskProject');
217
+ const addTaskBtn = document.getElementById('addTaskBtn');
218
+ const taskList = document.getElementById('taskList');
219
+ const taskFilter = document.getElementById('taskFilter');
220
+ const completedTasksCount = document.getElementById('completedTasksCount');
221
+ const totalTimeElement = document.getElementById('totalTime');
222
+ const totalValueElement = document.getElementById('totalValue');
223
+ const taskTemplate = document.getElementById('taskTemplate');
224
+
225
+ // State
226
+ let tasks = JSON.parse(localStorage.getItem('garTimeTasks')) || [];
227
+ let activeTimer = null;
228
+ let isDarkMode = localStorage.getItem('darkMode') === 'true';
229
+
230
+ // Initialize
231
+ updateUI();
232
+ updateStats();
233
+ applyDarkMode();
234
+
235
+ // Event Listeners
236
+ darkModeToggle.addEventListener('click', toggleDarkMode);
237
+ addTaskBtn.addEventListener('click', addTask);
238
+ taskFilter.addEventListener('change', filterTasks);
239
+ hourlyRateInput.addEventListener('change', updateHourlyRate);
240
+ mobileHourlyRateInput.addEventListener('change', updateHourlyRate);
241
+
242
+ // Functions
243
+ function toggleDarkMode() {
244
+ isDarkMode = !isDarkMode;
245
+ localStorage.setItem('darkMode', isDarkMode);
246
+ applyDarkMode();
247
+ }
248
+
249
+ function applyDarkMode() {
250
+ if (isDarkMode) {
251
+ document.body.classList.add('dark-mode');
252
+ document.body.classList.add('dark');
253
+ darkModeToggleCircle.classList.remove('translate-x-1');
254
+ darkModeToggleCircle.classList.add('translate-x-6');
255
+ } else {
256
+ document.body.classList.remove('dark-mode');
257
+ document.body.classList.remove('dark');
258
+ darkModeToggleCircle.classList.add('translate-x-1');
259
+ darkModeToggleCircle.classList.remove('translate-x-6');
260
+ }
261
+ }
262
+
263
+ function updateHourlyRate() {
264
+ const rate = parseFloat(hourlyRateInput.value) || 0;
265
+ hourlyRateInput.value = rate.toFixed(2);
266
+ mobileHourlyRateInput.value = rate.toFixed(2);
267
+ updateTasksValues();
268
+ updateStats();
269
+ saveTasks();
270
+ }
271
+
272
+ function addTask() {
273
+ const name = taskNameInput.value.trim();
274
+ const description = taskDescriptionInput.value.trim();
275
+ const project = taskProjectInput.value.trim();
276
+
277
+ if (!name) {
278
+ alert('Por favor ingresa un nombre para la tarea');
279
+ return;
280
+ }
281
+
282
+ const newTask = {
283
+ id: Date.now(),
284
+ name,
285
+ description,
286
+ project,
287
+ isCompleted: false,
288
+ timeSpent: 0, // in seconds
289
+ timerInterval: null,
290
+ createdAt: new Date().toISOString(),
291
+ lastActiveAt: null
292
+ };
293
+
294
+ tasks.push(newTask);
295
+ saveTasks();
296
+ updateUI();
297
+
298
+ // Clear form
299
+ taskNameInput.value = '';
300
+ taskDescriptionInput.value = '';
301
+ taskProjectInput.value = '';
302
+ taskNameInput.focus();
303
+ }
304
+
305
+ function updateUI() {
306
+ taskList.innerHTML = '';
307
+
308
+ if (tasks.length === 0) {
309
+ taskList.innerHTML = `
310
+ <div class="p-6 text-center text-gray-500 dark:text-gray-400">
311
+ <i class="fas fa-tasks text-4xl mb-2"></i>
312
+ <p>No hay tareas registradas</p>
313
+ <p class="text-sm">Agrega tu primera tarea usando el formulario</p>
314
+ </div>
315
+ `;
316
+ return;
317
+ }
318
+
319
+ const filteredTasks = filterTasksByStatus(taskFilter.value);
320
+
321
+ filteredTasks.forEach(task => {
322
+ const taskElement = createTaskElement(task);
323
+ taskList.appendChild(taskElement);
324
+ });
325
+ }
326
+
327
+ function filterTasksByStatus(status) {
328
+ if (status === 'all') return [...tasks].sort((a, b) => new Date(b.createdAt) - new Date(a.createdAt));
329
+ if (status === 'active') return tasks.filter(task => !task.isCompleted);
330
+ if (status === 'completed') return tasks.filter(task => task.isCompleted);
331
+ return tasks;
332
+ }
333
+
334
+ function filterTasks() {
335
+ updateUI();
336
+ }
337
+
338
+ function createTaskElement(task) {
339
+ const taskElement = taskTemplate.content.cloneNode(true);
340
+ const taskContainer = taskElement.querySelector('.task-item');
341
+
342
+ if (task.isCompleted) {
343
+ taskContainer.classList.add('task-completed');
344
+ }
345
+
346
+ // Set task data
347
+ taskElement.querySelector('.task-name').textContent = task.name;
348
+ taskElement.querySelector('.task-description').textContent = task.description || 'Sin descripción';
349
+ taskElement.querySelector('.task-project').textContent = task.project || 'Sin proyecto';
350
+ taskElement.querySelector('.task-time').textContent = formatTime(task.timeSpent);
351
+ taskElement.querySelector('.task-value').textContent = calculateTaskValue(task.timeSpent);
352
+ taskElement.querySelector('.task-date').textContent = formatDate(task.createdAt);
353
+
354
+ // Set timer button state
355
+ const timerBtn = taskElement.querySelector('.timer-toggle-btn');
356
+ const timerLabel = taskElement.querySelector('.timer-label');
357
+
358
+ if (task.timerInterval) {
359
+ timerBtn.classList.add('timer-active');
360
+ timerBtn.classList.remove('bg-blue-100', 'dark:bg-blue-900/30');
361
+ timerBtn.classList.add('bg-blue-600', 'text-white');
362
+ timerLabel.textContent = 'En progreso';
363
+ timerLabel.innerHTML = `<i class="fas fa-spinner fa-spin mr-1"></i> En progreso`;
364
+ } else {
365
+ timerBtn.classList.remove('timer-active');
366
+ timerLabel.textContent = 'Iniciar';
367
+ }
368
+
369
+ // Button event listeners
370
+ timerBtn.addEventListener('click', () => toggleTimer(task.id));
371
+ taskElement.querySelector('.complete-btn').addEventListener('click', () => toggleTaskCompletion(task.id));
372
+ taskElement.querySelector('.delete-btn').addEventListener('click', () => deleteTask(task.id));
373
+
374
+ return taskElement;
375
+ }
376
+
377
+ function toggleTimer(taskId) {
378
+ // Stop any active timer first
379
+ if (activeTimer) {
380
+ stopTimer(activeTimer);
381
+ }
382
+
383
+ const task = tasks.find(t => t.id === taskId);
384
+
385
+ if (task.timerInterval) {
386
+ stopTimer(taskId);
387
+ } else {
388
+ startTimer(taskId);
389
+ activeTimer = taskId;
390
+ }
391
+
392
+ updateUI();
393
+ saveTasks();
394
+ }
395
+
396
+ function startTimer(taskId) {
397
+ const task = tasks.find(t => t.id === taskId);
398
+
399
+ if (!task) return;
400
+
401
+ task.lastActiveAt = new Date().toISOString();
402
+
403
+ task.timerInterval = setInterval(() => {
404
+ task.timeSpent++;
405
+ updateStats();
406
+
407
+ // Update the UI for this specific task if it's visible
408
+ const taskElement = document.querySelector(`[data-task-id="${taskId}"]`);
409
+ if (taskElement) {
410
+ taskElement.querySelector('.task-time').textContent = formatTime(task.timeSpent);
411
+ taskElement.querySelector('.task-value').textContent = calculateTaskValue(task.timeSpent);
412
+ }
413
+ }, 1000);
414
+
415
+ saveTasks();
416
+ }
417
+
418
+ function stopTimer(taskId) {
419
+ const task = tasks.find(t => t.id === taskId);
420
+
421
+ if (!task || !task.timerInterval) return;
422
+
423
+ clearInterval(task.timerInterval);
424
+ task.timerInterval = null;
425
+
426
+ if (activeTimer === taskId) {
427
+ activeTimer = null;
428
+ }
429
+
430
+ saveTasks();
431
+ }
432
+
433
+ function toggleTaskCompletion(taskId) {
434
+ const task = tasks.find(t => t.id === taskId);
435
+
436
+ if (!task) return;
437
+
438
+ // Stop timer if running
439
+ if (task.timerInterval) {
440
+ stopTimer(taskId);
441
+ if (activeTimer === taskId) {
442
+ activeTimer = null;
443
+ }
444
+ }
445
+
446
+ task.isCompleted = !task.isCompleted;
447
+ updateUI();
448
+ updateStats();
449
+ saveTasks();
450
+ }
451
+
452
+ function deleteTask(taskId) {
453
+ if (!confirm('¿Estás seguro de que quieres eliminar esta tarea?')) return;
454
+
455
+ // Stop timer if running
456
+ const task = tasks.find(t => t.id === taskId);
457
+ if (task && task.timerInterval) {
458
+ stopTimer(taskId);
459
+ if (activeTimer === taskId) {
460
+ activeTimer = null;
461
+ }
462
+ }
463
+
464
+ tasks = tasks.filter(t => t.id !== taskId);
465
+ updateUI();
466
+ updateStats();
467
+ saveTasks();
468
+ }
469
+
470
+ function updateTasksValues() {
471
+ tasks.forEach(task => {
472
+ // Update is handled in the timer interval for active tasks
473
+ // For completed tasks, we don't need to do anything as value is calculated on demand
474
+ });
475
+
476
+ updateUI();
477
+ }
478
+
479
+ function updateStats() {
480
+ const completedTasks = tasks.filter(t => t.isCompleted).length;
481
+ const totalTime = tasks.reduce((sum, task) => sum + task.timeSpent, 0);
482
+ const totalValue = tasks.reduce((sum, task) => {
483
+ const hourlyRate = parseFloat(hourlyRateInput.value) || 0;
484
+ return sum + (task.timeSpent / 3600 * hourlyRate);
485
+ }, 0);
486
+
487
+ completedTasksCount.textContent = completedTasks;
488
+ totalTimeElement.textContent = formatTime(totalTime);
489
+ totalValueElement.textContent = `$${totalValue.toFixed(2)}`;
490
+ }
491
+
492
+ function formatTime(seconds) {
493
+ const hrs = Math.floor(seconds / 3600);
494
+ const mins = Math.floor((seconds % 3600) / 60);
495
+ const secs = seconds % 60;
496
+
497
+ return `${String(hrs).padStart(2, '0')}:${String(mins).padStart(2, '0')}:${String(secs).padStart(2, '0')}`;
498
+ }
499
+
500
+ function calculateTaskValue(seconds) {
501
+ const hourlyRate = parseFloat(hourlyRateInput.value) || 0;
502
+ const value = (seconds / 3600) * hourlyRate;
503
+ return `$${value.toFixed(2)}`;
504
+ }
505
+
506
+ function formatDate(isoString) {
507
+ const date = new Date(isoString);
508
+ return date.toLocaleDateString('es-ES', {
509
+ day: '2-digit',
510
+ month: '2-digit',
511
+ year: 'numeric',
512
+ hour: '2-digit',
513
+ minute: '2-digit'
514
+ });
515
+ }
516
+
517
+ function saveTasks() {
518
+ localStorage.setItem('garTimeTasks', JSON.stringify(tasks));
519
+ }
520
+
521
+ // Initialize tasks with data attributes for easier selection
522
+ document.addEventListener('click', function(e) {
523
+ if (e.target.closest('.task-item')) {
524
+ const taskElement = e.target.closest('.task-item');
525
+ const taskId = parseInt(taskElement.dataset.taskId);
526
+ // You can add more specific event handling here if needed
527
+ }
528
+ });
529
+
530
+ // Restore any active timers on page load
531
+ tasks.forEach(task => {
532
+ if (task.timerInterval) {
533
+ // Clear any existing interval (shouldn't exist on page load)
534
+ if (task.timerInterval) {
535
+ clearInterval(task.timerInterval);
536
+ }
537
+
538
+ // Calculate time passed since last active
539
+ if (task.lastActiveAt) {
540
+ const lastActive = new Date(task.lastActiveAt);
541
+ const now = new Date();
542
+ const secondsPassed = Math.floor((now - lastActive) / 1000);
543
+ task.timeSpent += secondsPassed;
544
+ }
545
+
546
+ // Restart the timer
547
+ startTimer(task.id);
548
+ }
549
+ });
550
+ });
551
+ </script>
552
+ <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=fgarola/gartime" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
553
+ </html>