taskninja 1.0.2 → 1.0.4

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 CHANGED
@@ -1,30 +1,23 @@
1
- # TaskNinja
2
-
3
- ![TaskNinja Logo](https://via.placeholder.com/150?text=TaskNinja) <!-- Optional: Replace with actual logo if available -->
4
-
5
- A simple Command-Line Interface (CLI) application built with JavaScript for managing your to-do tasks. It allows users to add, list, update, and delete tasks interactively, with features like status tracking, priority levels, due dates, and descriptions. Tasks are stored in a local JSON file for persistence.
6
1
 
7
- This project is designed for developers and users who prefer a lightweight, terminal-based task manager without the need for complex setups or databases.
8
-
9
- ## Features
2
+ # TaskNinja
10
3
 
11
- - **Add Tasks**: Create new tasks with title, status (todo, in-progress, done), priority (low, medium, high), due date, and optional description.
12
- - **List Tasks**: View all tasks or filter by status in a tabular format.
13
- - **Update Tasks**: Change the status of existing tasks via interactive selection.
14
- - **Delete Tasks**: Remove tasks with confirmation to prevent accidental deletion.
15
- - **Persistent Storage**: Tasks are saved to a local `todos.json` file.
16
- - **Interactive Prompts**: Uses Inquirer for user-friendly input.
17
- - **Validation**: Ensures valid statuses, priorities, and date formats.
4
+ **Version:** 1.0.4
5
+ **Description:** A simple CLI To-Do Application to manage your tasks directly from the terminal.
18
6
 
19
- ## Technologies Used
7
+ ---
20
8
 
21
- - **Node.js**: Runtime environment.
22
- - **Commander**: For parsing command-line arguments and defining commands.
23
- - **Inquirer**: For interactive CLI prompts.
24
- - **fs/promises**: For asynchronous file system operations to handle task storage.
25
- - **ECMAScript Modules (ESM)**: Modern JavaScript module system.
9
+ ## Table of Contents
10
+ - [Installation](#installation)
11
+ - [Running the CLI](#running-the-cli)
12
+ - [Commands](#commands)
13
+ - [Add Task](#add-task)
14
+ - [List Tasks](#list-tasks)
15
+ - [Update Task](#update-task)
16
+ - [Delete Task](#delete-task)
17
+ - [Task Fields & Allowed Values](#task-fields--allowed-values)
18
+ - [Examples](#examples)
26
19
 
27
- The application is written in pure JavaScript (ES6+), with no additional frameworks or databases required.
20
+ ---
28
21
 
29
22
  ## Installation
30
23
 
@@ -36,198 +29,221 @@ TaskNinja is published on npm as `taskninja`. Install it globally to use the CLI
36
29
  npm install -g taskninja
37
30
  ```
38
31
 
39
- After installation, you can run the CLI using the `dotask` command (as defined in `package.json`).
40
-
41
- ### From Source
42
-
43
- 1. Clone the repository:
44
- ```bash
45
- git clone https://github.com/yourusername/taskninja.git
46
- cd taskninja
47
- ```
48
-
49
- 2. Install dependencies:
50
- ```bash
51
- npm install
52
- ```
53
-
54
- 3. Link the CLI globally (optional, for testing):
55
- ```bash
56
- npm link
57
- ```
58
-
59
- 4. Run the application:
60
- ```bash
61
- npm start
62
- ```
63
- Or directly:
64
- ```bash
65
- node app.js
66
- ```
67
-
68
- ## Usage
69
-
70
- Run the CLI with the `dotask` command (after global installation) or `node app.js` from the source.
71
-
72
- ### Commands
73
-
74
- - **Add a Task**:
75
- ```bash
76
- dotask add
77
- ```
78
- - This will prompt you interactively for title, status, priority, due date, and description.
79
- - Example output: "Task added successfully!"
80
-
81
- - **List Tasks**:
82
- ```bash
83
- dotask list
84
- ```
85
- - Lists all tasks in a table format.
86
- - Optional filter by status: `dotask list --status todo` (or `-s todo`).
87
- - Aliases: `dotask ls`.
88
-
89
- - **Update a Task**:
90
- ```bash
91
- dotask update
92
- ```
93
- - Prompts to select a task by ID and update its status.
94
- - Displays the updated task in a table.
95
- - Aliases: `dotask up`.
96
-
97
- - **Delete a Task**:
98
- ```bash
99
- dotask delete
100
- ```
101
- - Prompts to select a task by ID and confirm deletion.
102
- - Aliases: `dotask del`.
103
-
104
- - **Help**:
105
- ```bash
106
- dotask --help
107
- ```
108
- - Shows all available commands and options.
109
-
110
- - **Version**:
111
- ```bash
112
- dotask --version
113
- ```
114
- - Displays the current version (1.0.0).
115
-
116
- ### Examples
117
-
118
- 1. Adding a task:
119
- ```
120
- dotask add
121
- ```
122
- - Follow prompts: Enter "Buy groceries" as title, select "todo" status, "medium" priority, "2024-12-31" due date, and "Milk, eggs, bread" as description.
123
-
124
- 2. Listing tasks filtered by status:
125
- ```
126
- dotask list -s in-progress
127
- ```
128
-
129
- 3. Updating a task:
130
- ```
131
- dotask update
132
- ```
133
- - Select task ID from the list and choose new status (e.g., "done").
134
-
135
- 4. Deleting a task:
136
- ```
137
- dotask delete
138
- ```
139
- - Select task ID and confirm.
140
-
141
- Tasks are stored in `./todos.json` relative to where the command is run. Ensure write permissions in the directory.
142
-
143
- ## Configuration
144
-
145
- - **Task File**: Defaults to `./todos.json`. You can modify `TASKS_FILE` in `app.js` if needed.
146
- - **Statuses**: Limited to "todo", "in-progress", "done".
147
- - **Priorities**: Limited to "low", "medium", "high".
148
- - **Due Date Format**: YYYY-MM-DD (validated during input).
149
-
150
- No external configuration files are required.
151
-
152
- ## Development
153
-
154
- To run in development mode:
32
+ or use it instantly with:
33
+ ```bash
34
+ npx taskninja
35
+ ```
36
+
37
+ and then use it:
38
+ ```bash
39
+ npx taskninja <command>
40
+ ```
41
+
42
+ After installation, you can run the CLI using the `dotask` or `taskninja` or `tn` command (as defined in `package.json`).
43
+
44
+ Clone the repository and install dependencies:
45
+
46
+ ```bash
47
+ git clone <your-repo-url>
48
+ cd taskninja
49
+ npm install
50
+ ````
51
+
52
+ ---
53
+
54
+ ## Running the CLI
55
+
56
+ You can run the CLI using either `node` or the bin aliases:
57
+
58
+ ```bash
59
+ # Using node
60
+ node app.js <command>
61
+
62
+ # Using bin aliases
63
+ dotask <command>
64
+ taskninja <command>
65
+ tn <command>
66
+ ```
67
+
68
+ Replace `<command>` with any of the available commands: `add`, `list`, `update`, `delete`.
69
+
70
+ ---
71
+
72
+ ## Commands
73
+
74
+ ### 1. Add Task
75
+
76
+ **Alias:** `a`
77
+ **Description:** Add a new task interactively.
78
+
79
+ ```bash
80
+ node app.js add
81
+ # or
82
+ tn a
83
+ ```
84
+
85
+ **Prompts:**
86
+
87
+ 1. **Task Title:** Enter any text (cannot be empty).
88
+ 2. **Task Status:** Select from allowed values using arrows:
89
+
90
+ * todo
91
+ * in-progress
92
+ * done
93
+ 3. **Task Priority:** Select from allowed values using arrows:
94
+
95
+ * low
96
+ * medium
97
+ * high
98
+ 4. **Due Date:** Enter in `YYYY-MM-DD` format. Invalid dates will show a validation error.
99
+ 5. **Description:** Optional text.
100
+
101
+ **Expected Behavior:**
102
+
103
+ * Task is saved in `todos.json`.
104
+ * Confirmation message: `Task added successfully!`
105
+
106
+ ---
107
+
108
+ ### 2. List Tasks
109
+
110
+ **Alias:** `ls`
111
+ **Description:** List all tasks, optionally filtered by status.
112
+
113
+ ```bash
114
+ node app.js list
115
+ # or
116
+ tn ls
117
+ ```
118
+
119
+ **Optional Status Filter:**
120
+
155
121
  ```bash
156
- npm run dev
122
+ node app.js list --status todo
157
123
  ```
158
124
 
159
- This executes `node app.js` for quick testing.
125
+ **Behavior:**
160
126
 
161
- ### Scripts
127
+ * Displays tasks in a table with columns:
162
128
 
163
- - `npm start`: Runs the app.
164
- - `npm test`: Placeholder for tests (currently errors out; add tests as needed).
129
+ | # | ID | Title | Status | Priority | DueDate | Description |
130
+ | - | -- | ----- | ------ | -------- | ------- | ----------- |
165
131
 
166
- ## Contribution Guidelines
132
+ * Numbered index (`#`) starts from 1.
167
133
 
168
- We welcome contributions to improve TaskNinja! Whether it's bug fixes, new features, or documentation enhancements, follow these steps:
134
+ ---
169
135
 
170
- ### How to Contribute
136
+ ### 3. Update Task
171
137
 
172
- 1. **Fork the Repository**:
173
- - Go to the [GitHub repo](https://github.com/yourusername/taskninja) and click "Fork".
138
+ **Alias:** `up`
139
+ **Description:** Update an existing task by selecting its ID.
174
140
 
175
- 2. **Clone Your Fork**:
176
- ```bash
177
- git clone https://github.com/yourusername/taskninja.git
178
- cd taskninja
179
- ```
141
+ ```bash
142
+ node app.js update
143
+ # or
144
+ tn up
145
+ ```
180
146
 
181
- 3. **Create a Branch**:
182
- - Use a descriptive name: `git checkout -b feature/new-feature` or `git checkout -b fix/bug-fix`.
147
+ **Steps:**
183
148
 
184
- 4. **Make Changes**:
185
- - Follow the existing code style (ESLint can be added later for consistency).
186
- - Add or update tests if applicable.
187
- - Ensure your code handles errors gracefully and maintains validation logic.
149
+ 1. Select task by ID from a list (use arrows).
150
+ 2. Confirm which fields to change (title, status, priority, due date, description).
151
+ 3. Enter new values where applicable (status/priority selections use arrows).
188
152
 
189
- 5. **Commit Changes**:
190
- - Use clear commit messages: e.g., "feat: add search functionality" or "fix: resolve date validation issue".
191
- - Reference issues if relevant: e.g., "Closes #123".
153
+ **Behavior:**
192
154
 
193
- 6. **Push to Your Fork**:
194
- ```bash
195
- git push origin feature/new-feature
196
- ```
155
+ * Only fields selected for change are updated.
156
+ * Confirmation message: `Task updated successfully!`
157
+ * Shows updated task table.
197
158
 
198
- 7. **Open a Pull Request (PR)**:
199
- - Go to the original repo and click "New Pull Request".
200
- - Provide a detailed description of your changes, including why they're needed and any relevant screenshots.
201
- - Link to any related issues.
159
+ ---
202
160
 
203
- ### Guidelines
161
+ ### 4. Delete Task
204
162
 
205
- - **Code Style**: Use consistent indentation (2 spaces), semicolons, and follow ES6+ best practices.
206
- - **Testing**: Add unit tests using a framework like Jest if expanding features. Currently, no tests are implemented—contributions here are encouraged!
207
- - **Dependencies**: Keep them minimal. Justify any new additions.
208
- - **Features**: New commands or options should align with the simple CLI philosophy. Discuss major changes in an issue first.
209
- - **Documentation**: Update README.md with any new features or changes.
210
- - **Issues**: Check for existing issues before creating a new one. Use labels like "bug", "enhancement", or "question".
163
+ **Alias:** `del`
164
+ **Description:** Delete a task by selecting its ID.
211
165
 
212
- ### Code of Conduct
166
+ ```bash
167
+ node app.js delete
168
+ # or
169
+ tn del
170
+ ```
213
171
 
214
- - Be respectful and inclusive.
215
- - No harassment or discrimination.
216
- - Report issues to the maintainer (Mohamed Bakr).
172
+ **Steps:**
217
173
 
218
- By contributing, you agree that your contributions will be licensed under the ISC License.
174
+ 1. Select task by ID from a list (use arrows).
175
+ 2. Confirm deletion.
219
176
 
220
- ## License
177
+ **Behavior:**
221
178
 
222
- This project is licensed under the ISC License.
179
+ * Task is removed from `todos.json`.
180
+ * Confirmation message: `Task deleted successfully!`
181
+ * Shows updated task table.
182
+
183
+ ---
184
+
185
+ ## Task Fields & Allowed Values
186
+
187
+ | Field | Allowed Values / Description |
188
+ | ----------- | ---------------------------- |
189
+ | title | Any non-empty string |
190
+ | status | todo, in-progress, done |
191
+ | priority | low, medium, high |
192
+ | dueDate | YYYY-MM-DD |
193
+ | description | Optional text |
194
+
195
+ ---
196
+
197
+ ## Examples
198
+
199
+ **Adding a Task:**
200
+
201
+ ```bash
202
+ tn a
203
+ ```
223
204
 
224
- ## Author
205
+ ```
206
+ Task Title: Buy groceries
207
+ Task Status: → todo
208
+ Task Priority: → medium
209
+ Due Date (YYYY-MM-DD): 2026-02-01
210
+ Task Description (optional): Buy fruits and vegetables
211
+ ```
212
+
213
+ **Listing Tasks:**
214
+
215
+ ```bash
216
+ tn ls
217
+ ```
218
+
219
+ ```
220
+ ┌─┬────┬────────────────┬─────────────┬─────────┬────────────┬─────────────────────────────┐
221
+ │#│ ID │ Title │ Status │ Priority│ DueDate │ Description │
222
+ ├─┼────┼────────────────┼─────────────┼─────────┼────────────┼─────────────────────────────┤
223
+ │1│ 1 │ Buy groceries │ todo │ medium │ 2026-02-01 │ Buy fruits and vegetables │
224
+ └─┴────┴────────────────┴─────────────┴─────────┴────────────┴─────────────────────────────┘
225
+ ```
226
+
227
+ **Updating a Task:**
228
+
229
+ ```bash
230
+ tn up
231
+ ```
232
+
233
+ * Select task ID → confirm fields → update values
234
+ * Shows updated table.
235
+
236
+ **Deleting a Task:**
237
+
238
+ ```bash
239
+ tn del
240
+ ```
225
241
 
226
- - **Mohamed Bakr** - Just a Developer - [GitHub Profile](https://github.com/MoBMoCaffeine)
242
+ * Select task ID confirm deletion updated table shown
227
243
 
228
- ## Acknowledgments
244
+ ---
229
245
 
230
- - Inspired by simple CLI tools like Todoist CLI.
231
- - Thanks to the open-source community for libraries like Commander and Inquirer.
246
+ ## Notes
232
247
 
233
- If you encounter issues or have suggestions, open an issue on GitHub!
248
+ * All selection prompts (`status`, `priority`, task selection) use arrow keys in terminal.
249
+ * Task table always shows numbered index (`#`) starting from 1 for easy reference.
package/app.js CHANGED
@@ -1,5 +1,4 @@
1
1
  #! /usr/bin/env node
2
-
3
2
  /**
4
3
  * Task Manager CLI Application
5
4
  * This application allows users to manage their tasks via command line interface.
@@ -17,70 +16,28 @@
17
16
  import { Command } from "commander";
18
17
  // for interactive command line prompts
19
18
  import inquirer from "inquirer";
20
- // for file system operations
21
- import fs from "fs/promises";
22
19
 
23
20
  // assigning Commander to a variable
24
21
  const program = new Command();
25
22
 
26
- // file to store tasks
27
- const TASKS_FILE = "./todos.json";
28
- // allowed task statuses
29
- const ALLOWED_STATUSES = ["todo", "in-progress", "done"];
30
- // allowed task priorities
31
- const ALLOWED_PRIORITIES = ["low", "medium", "high"];
32
-
33
-
34
- // function to load tasks from file
35
- const loadTasks = async () => {
36
- try{
37
- const data = await fs.readFile(TASKS_FILE, 'utf8');
38
- return JSON.parse(data);
39
- } catch (error) {
40
- if (error.code === 'ENOENT') return [];
41
- throw error;
42
- }
43
- };
44
- // function to save tasks to file
45
- const saveTasks = async (tasks) => {
46
- await fs.writeFile(TASKS_FILE, JSON.stringify(tasks, null, 2));
47
- };
48
- // get next task ID
49
- const getNextId = (tasks) => {
50
- return tasks.length > 0 ? Math.max(...tasks.map(task => task.id)) + 1 : 1;
51
- };
52
- // validate task status
53
- const validateStatus = (status) => {
54
- if (!ALLOWED_STATUSES.includes(status)) {
55
- throw new Error(`Invalid status. Allowed statuses are: ${ALLOWED_STATUSES.join(", ")}`);
56
- }
57
- };
58
- // validate task priority
59
- const validatePriority = (priority) => {
60
- if (!ALLOWED_PRIORITIES.includes(priority)) {
61
- throw new Error(`Invalid priority. Allowed priorities are: ${ALLOWED_PRIORITIES.join(", ")}`);
62
- }
63
- };
64
- // verify due date format
65
- const validateDueDate = (dueDate) => {
66
- if (isNaN(Date.parse(dueDate))) {
67
- throw new Error("Invalid due date. Please use a valid date format (YYYY-MM-DD).");
68
- }
69
- };
23
+ // importing validators and allowed values
24
+ import { validateDueDate, ALLOWED_PRIORITIES, ALLOWED_STATUSES } from "./validators.js";
25
+ // importing task service functions
26
+ import { loadTasks, saveTasks, getNextId } from "./taskService.js";
70
27
 
71
28
 
72
29
  // setting up
73
30
  program
74
31
  .name("todo-cli")
75
32
  .description("A simple CLI application to manage your tasks")
76
- .version("1.0.0");
33
+ .version("1.0.4");
77
34
 
78
35
  // use command 'add' with title + status + priority + dueDate + description and action
79
36
  program
80
37
  .command('add')
81
38
  .alias('a')
82
39
  .description('Add a new task')
83
- .action(async (title, status, priority, dueDate, description = "") => {
40
+ .action(async () => {
84
41
  const answers = await inquirer.prompt([
85
42
  {
86
43
  type: 'input',
@@ -89,14 +46,14 @@ program
89
46
  validate : input => input ? true : 'Title cannot be empty!'
90
47
  },
91
48
  {
92
- type: 'list',
49
+ type: 'rawlist',
93
50
  name: 'status',
94
51
  message : 'Task Status:',
95
52
  choices : ALLOWED_STATUSES,
96
53
  default : 'todo'
97
54
  },
98
55
  {
99
- type: 'list',
56
+ type: 'rawlist',
100
57
  name: 'priority',
101
58
  message : 'Task Priority:',
102
59
  choices : ALLOWED_PRIORITIES,
@@ -146,7 +103,7 @@ program
146
103
  .command('list')
147
104
  .alias('ls')
148
105
  .description('List all tasks')
149
- .option('-s, --status <status>', 'Filter tasks by status')
106
+ .option('-s, --status <status>', 'Filter tasks by status (todo, in-progress, done)')
150
107
  .action(async (options) => {
151
108
  let tasks = await loadTasks();
152
109
 
@@ -162,7 +119,8 @@ program
162
119
  return;
163
120
  }
164
121
  // map all tasks to displayable objects
165
- const tableData = tasks.map(task => ({
122
+ const tableData = tasks.map((task, index) => ({
123
+ '#': index + 1,
166
124
  ID: task.id,
167
125
  Title: task.title,
168
126
  Status: task.status,
@@ -179,7 +137,7 @@ program
179
137
  program
180
138
  .command('update')
181
139
  .alias('up')
182
- .description('Update a task by ID')
140
+ .description('Update a task by ==> ID <==')
183
141
  .action(async () =>{
184
142
  const tasks = await loadTasks();
185
143
  if (tasks.length === 0) {
@@ -189,46 +147,148 @@ program
189
147
 
190
148
  const { id } = await inquirer.prompt([
191
149
  {
192
- type: 'list',
150
+ type: 'rawlist',
193
151
  name: 'id',
194
- message: 'Select the task to update:',
152
+ message: 'Select the task to update (By ID):',
195
153
  choices: tasks.map(task => ({ name: `${task.id}: ${task.title}`, value: task.id }))
196
154
  }
197
155
  ]);
198
- const { status } = await inquirer.prompt([
199
- {
200
- type: 'list',
201
- name: 'status',
202
- message: 'Select the new status:',
203
- choices: ALLOWED_STATUSES
204
- }
205
- ]);
206
156
 
157
+ // find the task to update
207
158
  const task = tasks.find(t => t.id === Number(id));
208
159
  if (!task) {
209
160
  console.error('Task not found!');
210
161
  return;
211
162
  }
212
- task.status = status;
213
163
 
214
- await saveTasks(tasks);
215
- console.log('Task updated successfully!');
164
+ const answers = await inquirer.prompt([
165
+ // for title
166
+ {
167
+ type: 'confirm',
168
+ name: 'changeTitle',
169
+ message: 'Do you want to change the title?',
170
+ default: false
171
+ },
172
+ {
173
+ type: 'input',
174
+ name: 'title',
175
+ message: 'Enter the new title:',
176
+ when: answers => answers.changeTitle,
177
+ validate: input => input ? true : 'Title cannot be empty!'
178
+ },
179
+
180
+ // for status
181
+ {
182
+ type: 'confirm',
183
+ name: 'changeStatus',
184
+ message: 'Do you want to change the status?',
185
+ default: false
186
+ },
187
+ {
188
+ type: 'rawlist',
189
+ name: 'status',
190
+ message: 'Select the new status:',
191
+ choices: ALLOWED_STATUSES,
192
+ when: answers => answers.changeStatus
193
+ },
194
+
195
+ // for priority
196
+ {
197
+ type: 'confirm',
198
+ name: 'changePriority',
199
+ message: 'Do you want to change the priority?',
200
+ default: false
201
+ },
202
+ {
203
+ type: 'rawlist',
204
+ name: 'priority',
205
+ message: 'Select the new priority:',
206
+ choices: ALLOWED_PRIORITIES,
207
+ when: answers => answers.changePriority
208
+ },
209
+
210
+ // for due date
211
+ {
212
+ type: 'confirm',
213
+ name: 'changeDueDate',
214
+ message: 'Do you want to change the due date?',
215
+ default: false
216
+ },
217
+ {
218
+ type: 'input',
219
+ name: 'dueDate',
220
+ message: 'Enter the new due date (YYYY-MM-DD):',
221
+ when: answers => answers.changeDueDate,
222
+ validate: input => {
223
+ try {
224
+ validateDueDate(input);
225
+ return true;
226
+ } catch (error) {
227
+ return error.message;
228
+ }
229
+ }
230
+ },
231
+
232
+ // for description
233
+ {
234
+ type: 'confirm',
235
+ name: 'changeDescription',
236
+ message: 'Do you want to change the description?',
237
+ default: false
238
+ },
239
+ {
240
+ type: "rawlist",
241
+ name: 'description',
242
+ message: 'Enter the new description:',
243
+ when: answers => answers.changeDescription
244
+ }
245
+ ]);
246
+
247
+ // apply updates only if user chose to change them
248
+ if (answers.changeTitle) task.title = answers.title;
249
+ if (answers.changeStatus) task.status = answers.status;
250
+ if (answers.changePriority) task.priority = answers.priority;
251
+ if (answers.changeDueDate) task.dueDate = answers.dueDate;
252
+ if (answers.changeDescription)
253
+ task.description = answers.description || '';
254
+
255
+ // is there any change?
256
+ const hasChanges = [
257
+ answers.changeTitle,
258
+ answers.changeStatus,
259
+ answers.changePriority,
260
+ answers.changeDueDate,
261
+ answers.changeDescription
262
+ ].some(Boolean);
216
263
 
217
- console.table([{
264
+ if (!hasChanges) {
265
+ console.log('No changes were made.');
266
+ }else {
267
+ // save updated tasks to file
268
+ await saveTasks(tasks);
269
+ console.log('Task updated successfully!');
270
+ }
271
+
272
+ const tableData = tasks.map((task, index) => ({
273
+ '#': index + 1,
218
274
  ID: task.id,
219
275
  Title: task.title,
220
276
  Status: task.status,
221
277
  Priority: task.priority,
222
278
  DueDate: task.dueDate,
223
279
  Description: task.description || ''
224
- }]);
280
+ }));
281
+
282
+ // display all tasks in table format
283
+ console.table(tableData);
225
284
  });
226
285
 
286
+
227
287
  // use command 'delete' with task ID and action
228
288
  program
229
289
  .command('delete')
230
290
  .alias('del')
231
- .description('delete a task by ID')
291
+ .description('delete a task by ==> ID <==')
232
292
  .action(async () => {
233
293
  const tasks = await loadTasks();
234
294
  if (tasks.length === 0) {
@@ -238,7 +298,7 @@ program
238
298
 
239
299
  const { id } = await inquirer.prompt([
240
300
  {
241
- type: 'list',
301
+ type: 'rawlist',
242
302
  name: 'id',
243
303
  message: 'Select the task to delete:',
244
304
  choices: tasks.map(task => ({ name: `${task.id}: ${task.title}`, value: task.id }))
@@ -258,18 +318,22 @@ program
258
318
  return;
259
319
  }
260
320
 
261
- const newTasks = tasks.filter(t => t.id !== id);
321
+ const newTasks = tasks.filter(t => t.id !== Number(id));
262
322
  await saveTasks(newTasks);
263
323
  console.log('Task deleted successfully!');
264
324
 
265
- console.table([{
325
+ const tableData = newTasks.map((task, index) => ({
326
+ '#': index + 1,
266
327
  ID: task.id,
267
328
  Title: task.title,
268
329
  Status: task.status,
269
330
  Priority: task.priority,
270
331
  DueDate: task.dueDate,
271
332
  Description: task.description || ''
272
- }]);
333
+ }));
334
+
335
+ // display all tasks in table format
336
+ console.table(tableData);
273
337
  });
274
338
 
275
339
  // parse command line arguments
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "taskninja",
3
- "version": "1.0.2",
3
+ "version": "1.0.4",
4
4
  "description": "a simple CLI application with JS as a (To-Do Application)",
5
5
  "main": "app.js",
6
6
  "type" : "module",
package/taskService.js ADDED
@@ -0,0 +1,25 @@
1
+ // for file system operations
2
+ import fs from "fs/promises";
3
+
4
+
5
+ // file to store tasks
6
+ const TASKS_FILE = "./todos.json";
7
+
8
+ // function to load tasks from file
9
+ export const loadTasks = async () => {
10
+ try{
11
+ const data = await fs.readFile(TASKS_FILE, 'utf8');
12
+ return JSON.parse(data);
13
+ } catch (error) {
14
+ if (error.code === 'ENOENT') return [];
15
+ throw error;
16
+ }
17
+ };
18
+ // function to save tasks to file
19
+ export const saveTasks = async (tasks) => {
20
+ await fs.writeFile(TASKS_FILE, JSON.stringify(tasks, null, 2));
21
+ };
22
+ // get next task ID
23
+ export const getNextId = (tasks) => {
24
+ return tasks.length > 0 ? Math.max(...tasks.map(task => task.id)) + 1 : 1;
25
+ };
package/todos.json ADDED
@@ -0,0 +1 @@
1
+ []
package/validators.js ADDED
@@ -0,0 +1,26 @@
1
+
2
+ // allowed task statuses
3
+ const ALLOWED_STATUSES = ["todo", "in-progress", "done"];
4
+ // allowed task priorities
5
+ const ALLOWED_PRIORITIES = ["low", "medium", "high"];
6
+
7
+ // validate task status
8
+ export const validateStatus = (status) => {
9
+ if (!ALLOWED_STATUSES.includes(status)) {
10
+ throw new Error(`Invalid status. Allowed statuses are: ${ALLOWED_STATUSES.join(", ")}`);
11
+ }
12
+ };
13
+ // validate task priority
14
+ export const validatePriority = (priority) => {
15
+ if (!ALLOWED_PRIORITIES.includes(priority)) {
16
+ throw new Error(`Invalid priority. Allowed priorities are: ${ALLOWED_PRIORITIES.join(", ")}`);
17
+ }
18
+ };
19
+ // verify due date format
20
+ export const validateDueDate = (dueDate) => {
21
+ if (isNaN(Date.parse(dueDate))) {
22
+ throw new Error("Invalid due date. Please use a valid date format (YYYY-MM-DD).");
23
+ }
24
+ };
25
+
26
+ export { ALLOWED_PRIORITIES, ALLOWED_STATUSES };