@beauraines/toggl-cli 2.2.1 → 2.3.0
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/CHANGELOG.md +7 -0
- package/cmds/addTimeEntry.mjs +68 -0
- package/cmds/edit.mjs +1 -19
- package/cmds/index.mjs +2 -0
- package/cmds/startTimeEntry.mjs +17 -2
- package/package.json +1 -1
- package/utils.js +29 -5
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
|
|
4
4
|
|
|
5
|
+
## [2.3.0](https://github.com/beauraines/toggl-cli/compare/v2.2.1...v2.3.0) (2024-05-25)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
### Features
|
|
9
|
+
|
|
10
|
+
* Add time entry, include start with create ([#165](https://github.com/beauraines/toggl-cli/issues/165)) ([d73f181](https://github.com/beauraines/toggl-cli/commit/d73f181115ea0a92e4e1427d0f2c77337ef55132))
|
|
11
|
+
|
|
5
12
|
### [2.2.1](https://github.com/beauraines/toggl-cli/compare/v2.2.0...v2.2.1) (2024-05-17)
|
|
6
13
|
|
|
7
14
|
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/* eslint-disable no-unused-expressions */
|
|
2
|
+
import Client from '../client.js'
|
|
3
|
+
import { defaultWorkspaceId, getProjectByName, getProjectById, appName, displayTimeEntry, parseTime } from '../utils.js'
|
|
4
|
+
import dayjs from 'dayjs'
|
|
5
|
+
import debugClient from 'debug'
|
|
6
|
+
import utc from 'dayjs/plugin/utc.js'
|
|
7
|
+
import timezone from 'dayjs/plugin/timezone.js'
|
|
8
|
+
dayjs.extend(utc)
|
|
9
|
+
dayjs.extend(timezone)
|
|
10
|
+
|
|
11
|
+
const debug = debugClient('toggl-cli-add');
|
|
12
|
+
|
|
13
|
+
export const command = 'add [startTime] [endTime] [description]'
|
|
14
|
+
export const desc = 'Create a time entry. Time must be parsable by dayjs, e.g. 4:50PM or \'12:00 AM\'.'
|
|
15
|
+
|
|
16
|
+
export const builder = {
|
|
17
|
+
d: { alias: ['description'], describe: 'Time entry name', type: 'string:', demandOption: true},
|
|
18
|
+
p: { alias: ['projectId', 'project'], describe: 'The case insensitive project name or project id.', type: 'string', demandOption: false },
|
|
19
|
+
s: { alias: ['start', 'startTime'], describe: 'The start time for the task, e.g. 13:00 12:45AM.', type: 'string', demandOption: false },
|
|
20
|
+
e: { alias: ['end', 'endTime'], describe: 'The end time for the task, e.g. 13:00 12:45AM.', type: 'string', demandOption: false }
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export const handler = async function (argv) {
|
|
24
|
+
const client = await Client()
|
|
25
|
+
const params = {}
|
|
26
|
+
|
|
27
|
+
params.workspace_id = +defaultWorkspaceId
|
|
28
|
+
let project
|
|
29
|
+
if (argv.projectId) {
|
|
30
|
+
if (isNaN(argv.projectId)) {
|
|
31
|
+
project = await getProjectByName(params.workspace_id, argv.projectId)
|
|
32
|
+
} else {
|
|
33
|
+
project = await getProjectById(params.workspace_id, argv.projectId)
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
let startTime, endTime
|
|
38
|
+
if (dayjs(argv.startTime).isValid()) {
|
|
39
|
+
startTime = argv.startTime
|
|
40
|
+
} else {
|
|
41
|
+
// Parse the time and set it based upon the current time
|
|
42
|
+
startTime = parseTime(argv.startTime)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (dayjs(argv.endTime).isValid()) {
|
|
46
|
+
endTime = argv.endTime
|
|
47
|
+
} else {
|
|
48
|
+
// Parse the time and set it based upon the current time
|
|
49
|
+
endTime = parseTime(argv.endTime)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
params.created_with = appName
|
|
53
|
+
project ? params.project_id = +project.id : undefined
|
|
54
|
+
startTime ? params.start = startTime.toISOString() : undefined
|
|
55
|
+
endTime ? params.stop = endTime.toISOString() : undefined
|
|
56
|
+
if (startTime || endTime) {
|
|
57
|
+
const startTimeUnix = startTime.unix()
|
|
58
|
+
const endTimeUnix = endTime.unix()
|
|
59
|
+
let duration = endTimeUnix - startTimeUnix
|
|
60
|
+
duration = endTime ? duration : startTimeUnix * -1
|
|
61
|
+
params.duration = duration
|
|
62
|
+
}
|
|
63
|
+
argv.description ? params.description = argv.description : undefined
|
|
64
|
+
debug(params)
|
|
65
|
+
const timeEntry = await client.timeEntries.create(params)
|
|
66
|
+
await displayTimeEntry(timeEntry)
|
|
67
|
+
}
|
|
68
|
+
|
package/cmds/edit.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/* eslint-disable no-unused-expressions */
|
|
2
2
|
import Client from '../client.js'
|
|
3
|
-
import { defaultWorkspaceId, getProjectByName, getProjectById, appName, displayTimeEntry } from '../utils.js'
|
|
3
|
+
import { defaultWorkspaceId, getProjectByName, getProjectById, appName, displayTimeEntry, parseTime } from '../utils.js'
|
|
4
4
|
import dayjs from 'dayjs'
|
|
5
5
|
import debugClient from 'debug'
|
|
6
6
|
import utc from 'dayjs/plugin/utc.js'
|
|
@@ -73,21 +73,3 @@ export const handler = async function (argv) {
|
|
|
73
73
|
const timeEntry = await client.timeEntries.update(currentTimeEntry.id, params)
|
|
74
74
|
await displayTimeEntry(timeEntry)
|
|
75
75
|
}
|
|
76
|
-
|
|
77
|
-
/**
|
|
78
|
-
* Parses a timelike string into a dayjs object of the current date and that time
|
|
79
|
-
* @param {string} timeString timelike string e.g. 4:50PM '12:00 AM' etc.
|
|
80
|
-
* @returns {object} dayjs object
|
|
81
|
-
*/
|
|
82
|
-
function parseTime (timeString) {
|
|
83
|
-
let h, m
|
|
84
|
-
// Assumes time in format 4:50 PM
|
|
85
|
-
const time = timeString.split(':', 2)
|
|
86
|
-
h = time[0]
|
|
87
|
-
m = time[1].match(/[0-9]+/)[0]
|
|
88
|
-
if (timeString.match(/PM/i) && h <= 12) {
|
|
89
|
-
// + in front of string converts to a number, cool!
|
|
90
|
-
h = +h + 12
|
|
91
|
-
}
|
|
92
|
-
return dayjs().hour(h).minute(m).second(0)
|
|
93
|
-
}
|
package/cmds/index.mjs
CHANGED
|
@@ -8,6 +8,7 @@ import * as edit from './edit.mjs'
|
|
|
8
8
|
import * as web from './web.mjs'
|
|
9
9
|
import * as startTimeEntry from './startTimeEntry.mjs'
|
|
10
10
|
import * as stopTimeEntry from './stopTimeEntry.mjs'
|
|
11
|
+
import * as addTimeEntry from './addTimeEntry.mjs'
|
|
11
12
|
import * as removeTimeEntry from './removeTimeEntry.mjs'
|
|
12
13
|
import * as today from './today.mjs'
|
|
13
14
|
import * as weekly from './weekly.mjs'
|
|
@@ -21,6 +22,7 @@ export const commands = [
|
|
|
21
22
|
projects,
|
|
22
23
|
startTimeEntry,
|
|
23
24
|
stopTimeEntry,
|
|
25
|
+
addTimeEntry,
|
|
24
26
|
removeTimeEntry,
|
|
25
27
|
today,
|
|
26
28
|
web,
|
package/cmds/startTimeEntry.mjs
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { defaultWorkspaceId,getProjectByName, createTimeEntry, getProjectById, defaultProjectId } from '../utils.js'
|
|
1
|
+
import { defaultWorkspaceId, getProjectByName, createTimeEntry, getProjectById, defaultProjectId, parseTime } from '../utils.js'
|
|
2
|
+
import dayjs from 'dayjs'
|
|
2
3
|
|
|
3
4
|
export const command = 'start'
|
|
4
5
|
export const desc = 'Starts a time entry'
|
|
@@ -10,7 +11,8 @@ export const builder = {
|
|
|
10
11
|
},
|
|
11
12
|
p: { alias: ['projectId', 'project'], describe: 'The case insensitive project name or project id.', type: 'string', demandOption: false },
|
|
12
13
|
// TODO default to default workspace
|
|
13
|
-
w: { alias: ['workspaceId', 'workspace'], describe: 'The case insensitive workspace name or workspace id.', type: 'number', demandOption: false }
|
|
14
|
+
w: { alias: ['workspaceId', 'workspace'], describe: 'The case insensitive workspace name or workspace id.', type: 'number', demandOption: false },
|
|
15
|
+
s: { alias: ['start', 'startTime'], describe: 'The start time for the task, e.g. 13:00 12:45AM.', type: 'string', demandOption: false },
|
|
14
16
|
}
|
|
15
17
|
|
|
16
18
|
export const handler = async function (argv) {
|
|
@@ -31,6 +33,19 @@ export const handler = async function (argv) {
|
|
|
31
33
|
}
|
|
32
34
|
}
|
|
33
35
|
|
|
36
|
+
if (argv.startTime) {
|
|
37
|
+
let startTime;
|
|
38
|
+
if (dayjs(argv.startTime).isValid()) {
|
|
39
|
+
startTime = argv.startTime
|
|
40
|
+
} else {
|
|
41
|
+
// Parse the time and set it based upon the current time
|
|
42
|
+
startTime = parseTime(argv.startTime)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
params.start = startTime.toISOString()
|
|
46
|
+
params.duration = -1
|
|
47
|
+
}
|
|
48
|
+
|
|
34
49
|
params.projectId = project?.id || defaultProjectId || null
|
|
35
50
|
// TODO check for invalid projectId or catch the error when creating fails
|
|
36
51
|
const timeEntry = await createTimeEntry(params)
|
package/package.json
CHANGED
package/utils.js
CHANGED
|
@@ -53,7 +53,7 @@ export const createTimeEntry = async function (params) {
|
|
|
53
53
|
description: params.description,
|
|
54
54
|
workspace_id: +params.workspaceId,
|
|
55
55
|
project_id: +params.projectId,
|
|
56
|
-
start: dayjs().toISOString(),
|
|
56
|
+
start: params.start ? params.start : dayjs().toISOString(),
|
|
57
57
|
duration: -1 * dayjs().unix(),
|
|
58
58
|
created_with: appName,
|
|
59
59
|
at: dayjs().toISOString()
|
|
@@ -113,11 +113,15 @@ export const displayTimeEntry = async function (timeEntry) {
|
|
|
113
113
|
console.info(`${chalk.blueBright(timeEntry.description ? timeEntry.description : 'no description')} ${chalk.yellow('#'+timeEntry.id)}`)
|
|
114
114
|
console.info(`Billable: ${chalk.gray(timeEntry.billable)}`)
|
|
115
115
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
116
|
+
let stopTime;
|
|
117
|
+
if (timeEntry.stop == null) {
|
|
118
|
+
stopTime = dayjs()
|
|
119
|
+
} else {
|
|
120
|
+
stopTime = dayjs(timeEntry.stop)
|
|
121
|
+
}
|
|
120
122
|
|
|
123
|
+
const duration = stopTime.diff(dayjs(timeEntry.start))
|
|
124
|
+
const durationFormatted = dayjs.duration(duration).format('H[h] m[m]')
|
|
121
125
|
console.info(`Duration: ${chalk.green(durationFormatted)}`)
|
|
122
126
|
|
|
123
127
|
const projects = await getProjects(timeEntry.wid)
|
|
@@ -138,3 +142,23 @@ export const displayTimeEntry = async function (timeEntry) {
|
|
|
138
142
|
console.info(`Workspace: ${workspace.name} (#${timeEntry.wid})`)
|
|
139
143
|
}
|
|
140
144
|
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Parses a timelike string into a dayjs object of the current date and that time
|
|
148
|
+
* @param {string} timeString timelike string e.g. 4:50PM '12:00 AM' etc.
|
|
149
|
+
* @returns {object} dayjs object
|
|
150
|
+
*/
|
|
151
|
+
export function parseTime (timeString) {
|
|
152
|
+
let h, m
|
|
153
|
+
// Assumes time in format 4:50 PM
|
|
154
|
+
const time = timeString.split(':', 2)
|
|
155
|
+
h = time[0]
|
|
156
|
+
m = time[1].match(/[0-9]+/)[0]
|
|
157
|
+
if (timeString.match(/PM/i) && h <= 12) {
|
|
158
|
+
// + in front of string converts to a number, cool!
|
|
159
|
+
h = +h + 12
|
|
160
|
+
} else if (h == 12) {
|
|
161
|
+
h = 0
|
|
162
|
+
}
|
|
163
|
+
return dayjs().hour(h).minute(m).second(0).millisecond(0)
|
|
164
|
+
}
|