|
// ==UserScript== |
|
// @name jira |
|
// @namespace https://gist.github.com/nfrigus/a026c7dfe4fb79b3373d2fcf1571f52d |
|
// @version 0.1.1 |
|
// @description Jira tools |
|
// @icon https://jira.time2go.tech/s/9urhrs/820030/1dlckms/_/images/fav-generic.png |
|
// @author [email protected] |
|
// @match https://jira.time2go.tech/* |
|
// @grant unsafeWindow |
|
// @updateURL https://gist.githubusercontent.com/nfrigus/a026c7dfe4fb79b3373d2fcf1571f52d/raw/jira.user.js |
|
// @downloadURL https://gist.githubusercontent.com/nfrigus/a026c7dfe4fb79b3373d2fcf1571f52d/raw/jira.user.js |
|
// ==/UserScript== |
|
(function () { |
|
'use strict' |
|
|
|
unsafeWindow.submitTimeLog = submitTimeLog |
|
|
|
async function mimicUserInput(el, text) { |
|
let set |
|
if(el instanceof HTMLInputElement) { |
|
set = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, "value").set |
|
} else if (el instanceof HTMLTextAreaElement) { |
|
set = Object.getOwnPropertyDescriptor(HTMLTextAreaElement.prototype, "value").set |
|
} else { |
|
throw new Error("Unsupported input type") |
|
} |
|
|
|
el.focus() |
|
for (const ch of text) { |
|
el.dispatchEvent(new KeyboardEvent("keydown", { key: ch, bubbles: true })) |
|
set.call(el, (el.value ?? "") + ch) |
|
el.dispatchEvent(new InputEvent("beforeinput", { inputType: "insertText", data: ch, bubbles: true, cancelable: true })) |
|
el.dispatchEvent(new InputEvent("input", { inputType: "insertText", data: ch, bubbles: true, composed: true })) |
|
el.dispatchEvent(new KeyboardEvent("keyup", { key: ch, bubbles: true })) |
|
} |
|
el.blur() |
|
el.dispatchEvent(new Event('change', { bubbles: true })) |
|
|
|
await Promise.resolve() |
|
} |
|
|
|
async function submitTimeLogGui(date, issue, spent, description) { |
|
// document.querySelector('[name="tempoCalendarLogWork"]').click() todo: add date select |
|
document.getElementById(date).querySelector('[name=tempoCalendarLogWork]').click() |
|
const issuePickerInput = await waitForSelector("#issuePickerInput") |
|
issuePickerInput.parentNode.childNodes[0].click() |
|
|
|
mimicUserInput(issuePickerInput, issue) |
|
const issuePickerDropdown = await waitForSelector(`#${issue}-search-1-row`) |
|
issuePickerDropdown.click() |
|
|
|
mimicUserInput(document.getElementById("timeSpentSeconds"), spent) |
|
mimicUserInput(document.getElementById("description"), description) |
|
|
|
await wait() |
|
|
|
document.getElementsByName("submitWorklogButton")[0].click() |
|
} |
|
|
|
async function wait(seconds = 1) { |
|
return new Promise(resolve => setTimeout(resolve, seconds * 1e3)) |
|
} |
|
|
|
async function waitForSelector(selector, context = document) { |
|
log("waitForSelector", selector) |
|
return waitFor(async () => context.querySelector(selector), context) |
|
} |
|
|
|
async function waitForXpath(xpath, context = document) { |
|
return waitFor(async () => document.evaluate(xpath, context, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue, context) |
|
} |
|
|
|
async function waitFor(handler, context = document) { |
|
return new Promise((resolve) => { |
|
new MutationObserver(async (_, observer) => { |
|
const target = await handler() |
|
if (target) { |
|
observer.disconnect() |
|
resolve(target) |
|
} |
|
}).observe(context, { |
|
childList: true, |
|
subtree: true |
|
}) |
|
}) |
|
} |
|
|
|
function log(...args) { |
|
console.log("[sm]", ...args) |
|
} |
|
|
|
|
|
async function getIssueId(issue) { |
|
return fetch("https://jira.time2go.tech/rest/api/2/search/", { |
|
"headers": { |
|
"accept": "application/json, application/vnd-ms-excel", |
|
"content-type": "application/json" |
|
}, |
|
"body": JSON.stringify({ |
|
"jql": `key = "${issue}"`, |
|
"fields": [], |
|
"startAt": 0, |
|
"maxResults": 1, |
|
"validateQuery": false |
|
}), |
|
"method": "POST", |
|
"mode": "cors", |
|
"credentials": "include" |
|
}).then(res => res.json()).then(result => result.issues[0].id) |
|
} |
|
|
|
async function submitTimeLog(date, issue, spent, description) { |
|
const id = await getIssueId(issue) |
|
await fetch("https://jira.time2go.tech/rest/tempo-timesheets/4/worklogs/", { |
|
"headers": { "content-type": "application/json" }, |
|
"referrer": "https://jira.time2go.tech/secure/Tempo.jspa", |
|
"body": JSON.stringify({ |
|
"attributes": {}, |
|
"billableSeconds": "", |
|
"originId": -1, |
|
"worker": document.getElementsByName("ajs-tempo-user-key")[0].content, |
|
"comment": description, |
|
"started": date, |
|
"timeSpentSeconds": +spent.replace("h", "") * 3600, |
|
"originTaskId": id, |
|
"remainingEstimate": 0, |
|
"endDate": null, |
|
"includeNonWorkingDays": false |
|
}), |
|
"method": "POST", |
|
"mode": "cors", |
|
"credentials": "include" |
|
}) |
|
} |
|
})() |