taskninja 1.1.1 → 1.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +38 -13
- package/app.js +58 -79
- package/helpers/helpers.js +70 -0
- package/images/addNew.png +0 -0
- package/images/allFeatures.png +0 -0
- package/images/deleteMethod.png +0 -0
- package/images/doneTask.png +0 -0
- package/images/doneTask2.png +0 -0
- package/images/listAll.png +0 -0
- package/images/searchWith.png +0 -0
- package/images/sortBy.png +0 -0
- package/images/sort_.png +0 -0
- package/images/todo.json.png +0 -0
- package/images/undoMethod.png +0 -0
- package/images/updateWith.png +0 -0
- package/package.json +1 -1
- package/utils/taskService.js +24 -4
- package/images/add.png +0 -0
- package/images/delete.png +0 -0
- package/images/delete2.png +0 -0
- package/images/done.png +0 -0
- package/images/done2.png +0 -0
- package/images/list.png +0 -0
- package/images/search.png +0 -0
- package/images/sort.png +0 -0
- package/images/sort2.png +0 -0
- package/images/undo.png +0 -0
- package/images/update.png +0 -0
package/README.md
CHANGED
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
* [Sort Tasks](#sort-tasks)
|
|
21
21
|
* [Task Fields & Allowed Values](#task-fields--allowed-values)
|
|
22
22
|
* [Examples](#examples)
|
|
23
|
+
* [Demo](#demo)
|
|
23
24
|
|
|
24
25
|
---
|
|
25
26
|
|
|
@@ -345,38 +346,62 @@ tn so
|
|
|
345
346
|
|
|
346
347
|
---
|
|
347
348
|
|
|
348
|
-
##
|
|
349
|
+
## Demo
|
|
350
|
+
|
|
351
|
+
**All Features:**
|
|
352
|
+
|
|
353
|
+

|
|
349
354
|
|
|
350
355
|
**Adding a Task:**
|
|
351
356
|
|
|
352
|
-

|
|
353
358
|
|
|
354
359
|
**Listing Tasks:**
|
|
355
360
|
|
|
356
|
-

|
|
357
362
|
|
|
358
363
|
**Updating a Task:**
|
|
359
364
|
|
|
360
|
-

|
|
361
366
|
|
|
362
367
|
**Deleting a Task:**
|
|
363
368
|
|
|
364
|
-

|
|
369
|
+

|
|
366
370
|
|
|
367
371
|
**Restore Last-Deleted Task:**
|
|
368
372
|
|
|
369
|
-

|
|
370
374
|
|
|
371
375
|
**Search / Sort Tasks:**
|
|
372
376
|
|
|
373
|
-

|
|
378
|
+

|
|
379
|
+

|
|
376
380
|
|
|
377
381
|
**Mark as Done:**
|
|
378
382
|
|
|
379
|
-

|
|
384
|
+

|
|
385
|
+
|
|
386
|
+
**Tasks in `todos.json`:**
|
|
387
|
+
|
|
388
|
+

|
|
389
|
+
|
|
390
|
+
|
|
391
|
+
---
|
|
392
|
+
|
|
393
|
+
## New Additions for Version `1.1.3`
|
|
394
|
+
|
|
395
|
+
**ESC Button End Operation:**
|
|
396
|
+
|
|
397
|
+
* If the user presses the `Esc` button, this will end the ongoing process
|
|
398
|
+
* This may benefit the user by terminating the process without having to complete the process until the end
|
|
399
|
+
|
|
400
|
+
### SOON
|
|
401
|
+
**Auto Deleted Task:**
|
|
402
|
+
|
|
403
|
+
* When you delete the task, it will remain saved in `deleted_todos.json` for one minute, after which it will be deleted automatically
|
|
404
|
+
* This somewhat conserves the user's storage space while calculating that the user may undo deleting a task
|
|
405
|
+
|
|
406
|
+
|
|
381
407
|
|
|
382
|
-
---
|
package/app.js
CHANGED
|
@@ -9,71 +9,34 @@
|
|
|
9
9
|
* - inquirer: For interactive prompts
|
|
10
10
|
* - fs: For file system operations
|
|
11
11
|
* Author: Mohamed Bakr
|
|
12
|
-
* Date: January
|
|
13
|
-
* Version: 1.1.
|
|
12
|
+
* Date: January 2026
|
|
13
|
+
* Version: 1.1.3
|
|
14
14
|
*/
|
|
15
15
|
|
|
16
16
|
// for using commands in terminal
|
|
17
17
|
import { Command } from "commander";
|
|
18
18
|
// for interactive command line prompts
|
|
19
19
|
import inquirer from "inquirer";
|
|
20
|
-
// supporting colors in table forms
|
|
21
|
-
import Table from 'cli-table3';
|
|
22
20
|
// for colored text
|
|
23
21
|
import chalk from 'chalk';
|
|
24
22
|
|
|
23
|
+
// helpers
|
|
24
|
+
import {displayTasks, enableEscExit, cleanupAndExit} from './helpers/helpers.js';
|
|
25
|
+
|
|
25
26
|
// assigning Commander to a variable
|
|
26
27
|
const program = new Command();
|
|
27
28
|
|
|
28
29
|
// importing validators and allowed values
|
|
29
30
|
import { validateDueDate, ALLOWED_PRIORITIES, ALLOWED_STATUSES } from "./utils/validators.js";
|
|
30
31
|
// importing task service functions
|
|
31
|
-
import { loadTasks, saveTasks, getNextId, saveDeletedTask, loadDeletedTask, clearDeletedTask
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
// helper function to display tasks in colored table
|
|
35
|
-
const displayTasks = (tasks) => {
|
|
36
|
-
if (!tasks.length) {
|
|
37
|
-
console.log(chalk.yellow("No tasks to display."));
|
|
38
|
-
return;
|
|
39
|
-
}
|
|
32
|
+
import { loadTasks, saveTasks, getNextId, saveDeletedTask, loadDeletedTask, clearDeletedTask, cleanupExpiredDeletedTasks } from "./utils/taskService.js";
|
|
40
33
|
|
|
41
|
-
const table = new Table({
|
|
42
|
-
head: [
|
|
43
|
-
chalk.cyanBright('#'),
|
|
44
|
-
chalk.cyanBright('ID'),
|
|
45
|
-
chalk.cyanBright('Title'),
|
|
46
|
-
chalk.cyanBright('Status'),
|
|
47
|
-
chalk.cyanBright('Priority'),
|
|
48
|
-
chalk.cyanBright('DueDate'),
|
|
49
|
-
chalk.cyanBright('Description')
|
|
50
|
-
],
|
|
51
|
-
colWidths: [4, 4, 30, 12, 10, 12, 60]
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
tasks.forEach((task, index) => {
|
|
55
|
-
table.push([
|
|
56
|
-
index + 1,
|
|
57
|
-
task.id,
|
|
58
|
-
task.title,
|
|
59
|
-
task.status === "done" ? chalk.green(task.status)
|
|
60
|
-
: task.status === "in-progress" ? chalk.yellow(task.status)
|
|
61
|
-
: chalk.blue(task.status),
|
|
62
|
-
task.priority === "high" ? chalk.red(task.priority)
|
|
63
|
-
: task.priority === "medium" ? chalk.yellow(task.priority)
|
|
64
|
-
: chalk.green(task.priority),
|
|
65
|
-
task.dueDate,
|
|
66
|
-
task.description || ''
|
|
67
|
-
]);
|
|
68
|
-
});
|
|
69
34
|
|
|
70
|
-
console.log(table.toString());
|
|
71
|
-
};
|
|
72
35
|
// setting up
|
|
73
36
|
program
|
|
74
37
|
.name("taskninja")
|
|
75
38
|
.description("A simple CLI application to manage your tasks")
|
|
76
|
-
.version("1.1.
|
|
39
|
+
.version("1.1.3");
|
|
77
40
|
|
|
78
41
|
// use command 'add' with title + status + priority + dueDate + description and action
|
|
79
42
|
program
|
|
@@ -81,6 +44,7 @@ program
|
|
|
81
44
|
.alias('a')
|
|
82
45
|
.description('Add a new task')
|
|
83
46
|
.action(async () => {
|
|
47
|
+
enableEscExit();
|
|
84
48
|
const answers = await inquirer.prompt([
|
|
85
49
|
{
|
|
86
50
|
type: 'input',
|
|
@@ -153,12 +117,13 @@ program
|
|
|
153
117
|
if (options.status) {
|
|
154
118
|
if (!ALLOWED_STATUSES.includes(options.status)){
|
|
155
119
|
console.log(chalk.red(`Invalid status filter. Allowed statuses are: ${ALLOWED_STATUSES.join(", ")}`));
|
|
156
|
-
|
|
120
|
+
cleanupAndExit(0);
|
|
157
121
|
}
|
|
158
122
|
tasks = tasks.filter(task => task.status === options.status);
|
|
159
123
|
}
|
|
160
124
|
// display all tasks in table format
|
|
161
125
|
displayTasks(tasks);
|
|
126
|
+
cleanupAndExit(0);
|
|
162
127
|
});
|
|
163
128
|
|
|
164
129
|
|
|
@@ -174,6 +139,7 @@ program
|
|
|
174
139
|
let searchTerm = keyword || options.find;
|
|
175
140
|
|
|
176
141
|
if (!searchTerm) {
|
|
142
|
+
enableEscExit();
|
|
177
143
|
const answers = await inquirer.prompt([
|
|
178
144
|
{
|
|
179
145
|
type: 'input',
|
|
@@ -196,16 +162,16 @@ program
|
|
|
196
162
|
if (field === 'title') return task.title.toLowerCase().includes(searchTerm.toLowerCase());
|
|
197
163
|
if (field === 'description') return task.description.toLowerCase().includes(searchTerm.toLowerCase());
|
|
198
164
|
return task.title.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
|
199
|
-
|
|
165
|
+
task.description.toLowerCase().includes(searchTerm.toLowerCase());
|
|
200
166
|
});
|
|
201
167
|
|
|
202
|
-
if (founded.length
|
|
168
|
+
if (!founded.length) {
|
|
203
169
|
console.log(chalk.red('No task matched your search!'));
|
|
204
|
-
|
|
170
|
+
cleanupAndExit(0);
|
|
205
171
|
}
|
|
206
172
|
|
|
207
173
|
displayTasks(founded);
|
|
208
|
-
|
|
174
|
+
cleanupAndExit(0);
|
|
209
175
|
}
|
|
210
176
|
|
|
211
177
|
const founded = tasks.filter(task =>
|
|
@@ -213,12 +179,13 @@ program
|
|
|
213
179
|
task.description.toLowerCase().includes(searchTerm.toLowerCase())
|
|
214
180
|
);
|
|
215
181
|
|
|
216
|
-
if (founded.length
|
|
182
|
+
if (!founded.length) {
|
|
217
183
|
console.log(chalk.red('No task matched your search!'));
|
|
218
|
-
|
|
184
|
+
cleanupAndExit(0);
|
|
219
185
|
}
|
|
220
186
|
|
|
221
187
|
displayTasks(founded);
|
|
188
|
+
cleanupAndExit(0);
|
|
222
189
|
});
|
|
223
190
|
|
|
224
191
|
|
|
@@ -236,6 +203,7 @@ program
|
|
|
236
203
|
let criteria = options.by;
|
|
237
204
|
|
|
238
205
|
if (!criteria) {
|
|
206
|
+
enableEscExit();
|
|
239
207
|
const answer = await inquirer.prompt([
|
|
240
208
|
{
|
|
241
209
|
type: 'rawlist',
|
|
@@ -246,33 +214,35 @@ program
|
|
|
246
214
|
]);
|
|
247
215
|
criteria = answer.criteria;
|
|
248
216
|
}
|
|
249
|
-
|
|
217
|
+
// normailze input `case-insensitive + separators`
|
|
218
|
+
criteria = criteria.toLowerCase().replace(/[-_]/g, '');
|
|
250
219
|
|
|
251
|
-
if (!['
|
|
220
|
+
if (!['duedate', 'priority', 'status'].includes(criteria)) {
|
|
252
221
|
console.log(chalk.red('Invalid sort criteria. Use --by with dueDate, priority, or status.'));
|
|
253
|
-
|
|
222
|
+
cleanupAndExit(0);
|
|
254
223
|
}
|
|
255
224
|
// sorting logic
|
|
256
225
|
const sortedTasks = [...tasks];
|
|
257
226
|
|
|
258
227
|
switch (criteria) {
|
|
259
|
-
case '
|
|
228
|
+
case 'duedate':
|
|
260
229
|
sortedTasks.sort((a, b) => new Date(a.dueDate) - new Date(b.dueDate));
|
|
261
230
|
break;
|
|
262
|
-
case 'priority'
|
|
231
|
+
case 'priority':
|
|
263
232
|
const priorityOrder = { 'high': 1, 'medium': 2, 'low': 3 };
|
|
264
233
|
sortedTasks.sort((a, b) => priorityOrder[a.priority] - priorityOrder[b.priority]);
|
|
265
234
|
break;
|
|
266
|
-
case 'status'
|
|
235
|
+
case 'status':
|
|
267
236
|
const statusOrder = { 'todo': 1, 'in-progress': 2, 'done': 3 };
|
|
268
|
-
sortedTasks.sort((a, b) => statusOrder[a.
|
|
237
|
+
sortedTasks.sort((a, b) => statusOrder[a.status] - statusOrder[b.status]);
|
|
269
238
|
break;
|
|
270
239
|
default:
|
|
271
240
|
console.log(chalk.red('Invalid sort criteria. Use dueDate, priority, or status.'));
|
|
272
|
-
|
|
241
|
+
cleanupAndExit(0);
|
|
273
242
|
}
|
|
274
243
|
|
|
275
244
|
displayTasks(sortedTasks);
|
|
245
|
+
cleanupAndExit(0);
|
|
276
246
|
});
|
|
277
247
|
|
|
278
248
|
|
|
@@ -283,11 +253,11 @@ program
|
|
|
283
253
|
.description('Update a task by ==> ID <==')
|
|
284
254
|
.action(async () =>{
|
|
285
255
|
const tasks = await loadTasks();
|
|
286
|
-
if (tasks.length
|
|
256
|
+
if (!tasks.length) {
|
|
287
257
|
console.log(chalk.red('No tasks found to update.'));
|
|
288
|
-
|
|
258
|
+
cleanupAndExit(0);
|
|
289
259
|
}
|
|
290
|
-
|
|
260
|
+
enableEscExit();
|
|
291
261
|
const { id } = await inquirer.prompt([
|
|
292
262
|
{
|
|
293
263
|
type: 'rawlist',
|
|
@@ -301,9 +271,10 @@ program
|
|
|
301
271
|
const task = tasks.find(t => t.id === Number(id));
|
|
302
272
|
if (!task) {
|
|
303
273
|
console.log(chalk.red('Task not found!'));
|
|
304
|
-
|
|
274
|
+
cleanupAndExit(0);
|
|
305
275
|
}
|
|
306
276
|
|
|
277
|
+
enableEscExit();
|
|
307
278
|
const answers = await inquirer.prompt([
|
|
308
279
|
// for title
|
|
309
280
|
{
|
|
@@ -380,10 +351,11 @@ program
|
|
|
380
351
|
default: false
|
|
381
352
|
},
|
|
382
353
|
{
|
|
383
|
-
type: "
|
|
354
|
+
type: "input",
|
|
384
355
|
name: 'description',
|
|
385
356
|
message: 'Enter the new description:',
|
|
386
|
-
when: answers => answers.changeDescription
|
|
357
|
+
when: answers => answers.changeDescription,
|
|
358
|
+
validate: input => input.trim() ? true : 'Description cannot be empty!'
|
|
387
359
|
}
|
|
388
360
|
]);
|
|
389
361
|
|
|
@@ -413,6 +385,7 @@ program
|
|
|
413
385
|
}
|
|
414
386
|
|
|
415
387
|
displayTasks(tasks);
|
|
388
|
+
cleanupAndExit(0);
|
|
416
389
|
});
|
|
417
390
|
|
|
418
391
|
|
|
@@ -422,17 +395,17 @@ program
|
|
|
422
395
|
.description('Mark a task as done by ==> ID <==')
|
|
423
396
|
.action(async () => {
|
|
424
397
|
const tasks = await loadTasks();
|
|
425
|
-
if (tasks.length
|
|
398
|
+
if (!tasks.length) {
|
|
426
399
|
console.log(chalk.magenta('Congratulations! All tasks are already done.'));
|
|
427
|
-
|
|
400
|
+
cleanupAndExit(0);
|
|
428
401
|
}
|
|
429
402
|
|
|
430
403
|
const activeTasks = tasks.filter(t => t.status !== 'done');
|
|
431
|
-
if (activeTasks.length
|
|
404
|
+
if (!activeTasks.length) {
|
|
432
405
|
console.log(chalk.magenta('Congratulations! All tasks are already done.'));
|
|
433
|
-
|
|
406
|
+
cleanupAndExit(0);
|
|
434
407
|
}
|
|
435
|
-
|
|
408
|
+
enableEscExit();
|
|
436
409
|
const { id } = await inquirer.prompt([
|
|
437
410
|
{
|
|
438
411
|
type: 'rawlist',
|
|
@@ -453,6 +426,7 @@ program
|
|
|
453
426
|
console.log(chalk.green('Task marked as done successfully!'));
|
|
454
427
|
|
|
455
428
|
displayTasks(tasks);
|
|
429
|
+
cleanupAndExit(0);
|
|
456
430
|
});
|
|
457
431
|
|
|
458
432
|
|
|
@@ -464,11 +438,11 @@ program
|
|
|
464
438
|
.description('delete a task by ==> ID <==')
|
|
465
439
|
.action(async () => {
|
|
466
440
|
const tasks = await loadTasks();
|
|
467
|
-
if (tasks.length
|
|
441
|
+
if (!tasks.length) {
|
|
468
442
|
console.log(chalk.yellow('No tasks found to delete.'));
|
|
469
|
-
|
|
443
|
+
cleanupAndExit(0);
|
|
470
444
|
}
|
|
471
|
-
|
|
445
|
+
enableEscExit();
|
|
472
446
|
const { id } = await inquirer.prompt([
|
|
473
447
|
{
|
|
474
448
|
type: 'rawlist',
|
|
@@ -477,7 +451,7 @@ program
|
|
|
477
451
|
choices: tasks.map(task => ({ name: `${task.id}: ${task.title}`, value: task.id }))
|
|
478
452
|
}
|
|
479
453
|
]);
|
|
480
|
-
|
|
454
|
+
enableEscExit();
|
|
481
455
|
const { confirm } = await inquirer.prompt([
|
|
482
456
|
{
|
|
483
457
|
type: 'confirm',
|
|
@@ -488,7 +462,7 @@ program
|
|
|
488
462
|
]);
|
|
489
463
|
if (!confirm) {
|
|
490
464
|
console.log(chalk.yellow('Task deletion cancelled.'));
|
|
491
|
-
|
|
465
|
+
cleanupAndExit(0);
|
|
492
466
|
}
|
|
493
467
|
const taskToDelete = tasks.find(t => t.id === Number(id));
|
|
494
468
|
// save deleted task for undo functionality
|
|
@@ -501,6 +475,7 @@ program
|
|
|
501
475
|
console.log(chalk.cyan('You can undo this action by using the `undo` command.'));
|
|
502
476
|
|
|
503
477
|
displayTasks(newTasks);
|
|
478
|
+
cleanupAndExit(0);
|
|
504
479
|
});
|
|
505
480
|
|
|
506
481
|
// use command 'undo' to restore last deleted task
|
|
@@ -509,17 +484,21 @@ program
|
|
|
509
484
|
.alias('un')
|
|
510
485
|
.description('Undo the last deleted task')
|
|
511
486
|
.action( async () => {
|
|
512
|
-
|
|
513
|
-
|
|
487
|
+
await cleanupExpiredDeletedTasks();
|
|
488
|
+
|
|
489
|
+
const deletedTasks = await loadDeletedTask();
|
|
490
|
+
if (!deletedTasks || !deletedTasks.length) {
|
|
514
491
|
console.log(chalk.yellow('No deleted task to restore.'));
|
|
515
|
-
|
|
492
|
+
cleanupAndExit(0);
|
|
516
493
|
}
|
|
517
494
|
|
|
495
|
+
const lastDeletedTask = deletedTasks.pop();
|
|
496
|
+
|
|
518
497
|
const tasks = await loadTasks();
|
|
519
498
|
tasks.push(lastDeletedTask);
|
|
520
499
|
tasks.sort((a, b) => a.id - b.id); // keep tasks sorted by ID
|
|
521
500
|
await saveTasks(tasks);
|
|
522
|
-
await clearDeletedTask();
|
|
501
|
+
// await clearDeletedTask();
|
|
523
502
|
|
|
524
503
|
console.log(chalk.green(`Last deleted task restored successfully!, (Task name: ${lastDeletedTask.title})`));
|
|
525
504
|
});
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
|
|
2
|
+
// supporting colors in table forms
|
|
3
|
+
import Table from 'cli-table3';
|
|
4
|
+
// for colored text
|
|
5
|
+
import chalk from 'chalk';
|
|
6
|
+
// for reading keyboard buttons
|
|
7
|
+
import readline from 'readline';
|
|
8
|
+
|
|
9
|
+
// helper function to display tasks in colored table
|
|
10
|
+
const displayTasks = (tasks) => {
|
|
11
|
+
if (!tasks.length) {
|
|
12
|
+
console.log(chalk.yellow("No tasks to display."));
|
|
13
|
+
cleanupAndExit(0);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const table = new Table({
|
|
17
|
+
head: [
|
|
18
|
+
chalk.cyanBright('#'),
|
|
19
|
+
chalk.cyanBright('ID'),
|
|
20
|
+
chalk.cyanBright('Title'),
|
|
21
|
+
chalk.cyanBright('Status'),
|
|
22
|
+
chalk.cyanBright('Priority'),
|
|
23
|
+
chalk.cyanBright('DueDate'),
|
|
24
|
+
chalk.cyanBright('Description')
|
|
25
|
+
],
|
|
26
|
+
colWidths: [4, 4, 20, 15, 10, 12, 45]
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
tasks.forEach((task, index) => {
|
|
30
|
+
table.push([
|
|
31
|
+
index + 1,
|
|
32
|
+
task.id,
|
|
33
|
+
task.title,
|
|
34
|
+
task.status === "done" ? chalk.green(task.status)
|
|
35
|
+
: task.status === "in-progress" ? chalk.yellow(task.status)
|
|
36
|
+
: chalk.blue(task.status),
|
|
37
|
+
task.priority === "high" ? chalk.red(task.priority)
|
|
38
|
+
: task.priority === "medium" ? chalk.yellow(task.priority)
|
|
39
|
+
: chalk.green(task.priority),
|
|
40
|
+
task.dueDate,
|
|
41
|
+
task.description || ''
|
|
42
|
+
]);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
console.log(table.toString());
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
const cleanupAndExit = (code = 0) => {
|
|
49
|
+
if (process.stdin.isTTY) {
|
|
50
|
+
process.stdin.setRawMode(false);
|
|
51
|
+
}
|
|
52
|
+
process.exit(code);
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
const enableEscExit = () => {
|
|
56
|
+
readline.emitKeypressEvents(process.stdin);
|
|
57
|
+
|
|
58
|
+
if (process.stdin.isTTY) {
|
|
59
|
+
process.stdin.setRawMode(true);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
process.stdin.on('keypress', (_, key) => {
|
|
63
|
+
if (key?.name === 'escape'){
|
|
64
|
+
console.log(chalk.yellow('\nOperation Cancelled!'));
|
|
65
|
+
cleanupAndExit(0);
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
export {displayTasks, enableEscExit, cleanupAndExit};
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/images/sort_.png
ADDED
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/package.json
CHANGED
package/utils/taskService.js
CHANGED
|
@@ -35,10 +35,6 @@ export const getNextId = (tasks) => {
|
|
|
35
35
|
* @description this section responsible to store deleted tasks in deleted_todos.json
|
|
36
36
|
* for undo functionality
|
|
37
37
|
*/
|
|
38
|
-
// to save deleted last-deleted task
|
|
39
|
-
export const saveDeletedTask = async (task) => {
|
|
40
|
-
await fs.writeFile(DELETED_TASKS_FILE, JSON.stringify(task, null, 2));
|
|
41
|
-
}
|
|
42
38
|
|
|
43
39
|
// to load deleted last-deleted task
|
|
44
40
|
export const loadDeletedTask = async () => {
|
|
@@ -50,6 +46,30 @@ export const loadDeletedTask = async () => {
|
|
|
50
46
|
throw error;
|
|
51
47
|
}
|
|
52
48
|
};
|
|
49
|
+
// to save deleted last-deleted task
|
|
50
|
+
export const saveDeletedTask = async (task, expiredAfter = 60000) => {
|
|
51
|
+
const deletedTask = await loadDeletedTask(task) || [];
|
|
52
|
+
const now = Date.now();
|
|
53
|
+
|
|
54
|
+
deletedTask.push({
|
|
55
|
+
...task,
|
|
56
|
+
deletedAt: now,
|
|
57
|
+
expiredAfter
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
await fs.writeFile(DELETED_TASKS_FILE, JSON.stringify(deletedTask, null, 2));
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// function to delete all deleted-tasks after a while
|
|
64
|
+
export const cleanupExpiredDeletedTasks = async () => {
|
|
65
|
+
const deletedTasks = await loadDeletedTask() || [];
|
|
66
|
+
const now = Date.now();
|
|
67
|
+
|
|
68
|
+
const remaining = deletedTasks.filter(task => now - task.deletedAt < task.expiredAfter);
|
|
69
|
+
if (remaining.length !== deletedTasks.length){
|
|
70
|
+
await fs.writeFile(DELETED_TASKS_FILE, JSON.stringify(remaining, null, 2));
|
|
71
|
+
}
|
|
72
|
+
}
|
|
53
73
|
|
|
54
74
|
// to clear deleted tasks file after undo
|
|
55
75
|
export const clearDeletedTask = async () => {
|
package/images/add.png
DELETED
|
Binary file
|
package/images/delete.png
DELETED
|
Binary file
|
package/images/delete2.png
DELETED
|
Binary file
|
package/images/done.png
DELETED
|
Binary file
|
package/images/done2.png
DELETED
|
Binary file
|
package/images/list.png
DELETED
|
Binary file
|
package/images/search.png
DELETED
|
Binary file
|
package/images/sort.png
DELETED
|
Binary file
|
package/images/sort2.png
DELETED
|
Binary file
|
package/images/undo.png
DELETED
|
Binary file
|
package/images/update.png
DELETED
|
Binary file
|