diff --git a/public/curation/click.js b/public/curation/click.js index 556a512..d0a9d07 100644 --- a/public/curation/click.js +++ b/public/curation/click.js @@ -16,4 +16,4 @@ function filterFunction() { a[i].style.display = "none"; } } -} \ No newline at end of file +} diff --git a/public/curation/conflict/index.html b/public/curation/conflict/index.html new file mode 100644 index 0000000..620b3eb --- /dev/null +++ b/public/curation/conflict/index.html @@ -0,0 +1,95 @@ + + + + + + + + + + Software CaRD + + + + + + + + + +

Curation Conflict

+ + +
+

See the conflict in the folling tables:

+
+ + + + + + + + + + + + + + + + +
CITATION.cff
@id + https://orcid.org/0000-0001-6372-3853 +
+ + + + + + + + + + + + + + + +
codemeta.md
@id + https://orcid.org/0000-0001-2345-6789 +
+
+
+

+ + +

+
+

Add comment to conflict

+ + +
+ + + diff --git a/public/curation/curation.js b/public/curation/curation.js index b29ee3b..80186cd 100644 --- a/public/curation/curation.js +++ b/public/curation/curation.js @@ -1,4 +1,5 @@ import { extract_info } from "./extract.js"; +import { addToBatch } from "./safe_comments.js"; /** * Fetches json_document and displays their contents in a table. @@ -102,6 +103,24 @@ export function displayJSON(json_document){ extract_info(mvalue, data[element], mtag, colorPolicies); tbody.appendChild(row); + + const slcomment = mvalue.querySelector("#single-line-comment"), + slcommentPopup = mvalue.querySelector("#single-line-comment-popup"); + const input = mvalue.querySelector("#comment"); + mvalue.querySelector('input[type="submit"]').addEventListener("click", () => { + addToBatch(element, data[element], input.value); + }); + slcomment.addEventListener('click', (event)=>{ + event.stopPropagation(); + console.log("clicked"); + slcommentPopup.style.visibility = "visible"; + }) + document.addEventListener('click', function(e) { + if ( slcommentPopup.style.visibility === "visible" && !slcommentPopup.contains(e.target) ) { + slcommentPopup.style.visibility = "hidden"; + } + }) + }) }) //Extend Checkbox for metadata source @@ -117,6 +136,8 @@ export function displayJSON(json_document){ } + + /** * Function to get a smaller object from the orginal object. * searches through data till skey and svalue match and return the data from this point. diff --git a/public/curation/extract.js b/public/curation/extract.js index 88d9123..6685e48 100644 --- a/public/curation/extract.js +++ b/public/curation/extract.js @@ -8,6 +8,8 @@ function extract_info(cell, obj, tag, colorPolicies){ element.style.color = colorPolicies[obj[2]["conflict"]]; element.appendChild(document.createTextNode(` ${obj[0]}`)); cell.appendChild(element); + console.log("parent: "+cell.parentNode) + //cell.parentNode.style.background = colorPolicies[obj[2]["conflict"]]; }else{ cell.appendChild(document.createTextNode(` ${obj[0]}`)); } @@ -53,12 +55,13 @@ function extract_info(cell, obj, tag, colorPolicies){ } function extract_person(e, element, tag, colorPolicies){ + let hasConfict = false; const tooltip = document.createElement("div"); const tooltiptag = document.createElement("div"); - tooltip.classList.add("tooltip"); + tooltip.classList.add("swc-tooltip"); tooltip.onclick = function(){link_to_person(e)}; const tooltiptext = document.createElement("div"); - tooltiptext.classList.add("tooltiptext"); + tooltiptext.classList.add("swc-tooltiptext"); const text = document.createTextNode(`${e.familyName[0]}, ${e.givenName[0]} `); tooltiptag.appendChild(document.createTextNode("See Details")); tooltiptag.appendChild(document.createElement("br")); @@ -77,8 +80,8 @@ function extract_info(cell, obj, tag, colorPolicies){ if(e[k][key][2] && e[k][key][2]["conflict"]){ pair_in_list.style.color = colorPolicies[e[k][key][2]["conflict"]]; tooltiptag.style.color = colorPolicies[e[k][key][2]["conflict"]]; - /*pair_in_list.className += " error"; - tooltiptag.className += " error";*/ + hasConfict = true; + } pair.appendChild(pair_in_list); } @@ -89,8 +92,7 @@ function extract_info(cell, obj, tag, colorPolicies){ if(e[k][2] && e[k][2]["conflict"]){ pair.style.color = colorPolicies[e[k][2]["conflict"]]; tooltiptag.style.color = colorPolicies[e[k][2]["conflict"]]; - //pair.className += " error"; - //tooltiptag.className += " error"; + hasConfict = true; } } tooltiptext.appendChild(pair); @@ -99,6 +101,7 @@ function extract_info(cell, obj, tag, colorPolicies){ tooltip.appendChild(tooltiptext); tooltip.appendChild(text); element.appendChild(tooltip); + return hasConfict; } function link_to_person(data){ diff --git a/public/curation/index.html b/public/curation/index.html index 09118ce..c7a58df 100644 --- a/public/curation/index.html +++ b/public/curation/index.html @@ -5,11 +5,10 @@ - + Software CaRD - @@ -19,6 +18,24 @@

Software CaRD +

Metadata

@@ -26,7 +43,7 @@

Software CaRD - - -
+
+ +
+

- +
@@ -54,6 +72,17 @@

Software CaRD

@@ -88,6 +117,7 @@

Policy Report

+ diff --git a/public/curation/safe_comments.js b/public/curation/safe_comments.js new file mode 100644 index 0000000..d386673 --- /dev/null +++ b/public/curation/safe_comments.js @@ -0,0 +1,5 @@ +import { addComment } from '../modules/storage.js' + +export async function addToBatch(value, data, comment){ + addComment(value, data, comment); +} diff --git a/public/curation/send_report.js b/public/curation/send_report.js new file mode 100644 index 0000000..6cb18cf --- /dev/null +++ b/public/curation/send_report.js @@ -0,0 +1,85 @@ +import { retrieveComment, retrievePipeline } from "../modules/storage.js"; +import { Octokit } from "https://esm.sh/@octokit/rest"; + + +const report = document.getElementById("sendReport"); +report.addEventListener("click", sendReport); +async function sendReport(){ + var message = "In [Software-CaRD](https://software-metadata.pub/software-card/) the following notes were added.\n"; + var comment = await retrieveComment(); + while (comment !== null) { + + message += ` +### ${comment.value} + +${comment.comment} +
+
+ +

Comment on this row

+ +
+
+
+
+
+ +
+
+ +
${comment.value}${json.stringify(comment.data[0])}
+ +`; + + comment = await retrieveComment(); + } + + console.log(message); + + + const token = localStorage.getItem("gitlab-api-token"); + const username = localStorage.getItem("gitlab-username"); + var [projectId, pipelineId, jobId] = await retrievePipeline(); + if(token.startsWith("glpat")){ + issueGitlab(token, projectId, message); + }else{ + issueGithub(token, username, message); + } +} + +async function issueGitlab(token, projectId, message){ +const response = await fetch( + `https://codebase.helmholtz.cloud/api/v4/projects/${projectId}/issues`, + { + method: "POST", + headers: { + "Content-Type": "application/json", + "PRIVATE-TOKEN": token + }, + body: JSON.stringify({ + title: "Software CaRD Report", + description: message + }) + } + ); + + const data = await response.json(); + +} +async function issueGithub(token, username, message){ + //TODO Test for Github +const octokit = new Octokit({ + auth: token + }) + +try{ + await octokit.request('POST /repos/SKernchen/SoftwareCaRD-Test/issues', { + owner: `${username}`, + repo: 'SoftwareCaRD-test', + title: `Curation Report`, + labels: ['curation'], + body: message, + headers: { + 'X-GitHub-Api-Version': '2022-11-28' + } + }) + alert("Send to GitHub"); + }catch(error){ + if (error.response) { + console.error(`Error! Status: ${error.response.status}. Message: ${error.response.data.message}`) + } + console.error(error) + document.body.innerHTML = `

Oooopsie

Your Report could not get sended to GitHub! Consider sending this:

${message}

another way.

+

Error message: ${error}

+ Back to Dashboard +
`; + } +} \ No newline at end of file diff --git a/public/modules/storage.js b/public/modules/storage.js index 0545a6b..8f28efd 100644 --- a/public/modules/storage.js +++ b/public/modules/storage.js @@ -8,6 +8,8 @@ function getDatabase() { var pipelineQueue = database.createObjectStore("PipelineQueue", { autoIncrement: true }); pipelineQueue.createIndex("PipelineIdIndex", ["pipelineId"]); + var commentQueue = database.createObjectStore("CommentQueue", { autoIncrement: true }); + commentQueue.createIndex("CommentIdIndex", ["commentId"]); // It would also be nice to have an index to return only entries where imported: false. // However, booleans are not valid keys. See: // https://w3c.github.io/IndexedDB/#key-construct @@ -92,6 +94,63 @@ async function retrievePipeline() { return new Promise(); } +function addCommentToQueue(queue, index, value, data, comment) { + return new Promise((resolve,reject) => { + queue.put({ + value: value, + data: data, + comment: comment, + imported: false + }); + resolve(); + }); +} + +async function addComment(value, data, comment) { + try { + var database = await getDatabase(); + var queue = await getDatabaseStore(database, "CommentQueue"); + var index = await getStoreIndex(queue, "CommentIdIndex"); + addCommentToQueue(queue, index, value, data, comment); + } catch (error) { + console.error(error); + } +} + +function getUnregisteredCommentFromQueue(queue) { + return new Promise((resolve, reject) => { + var comments = queue.openCursor(); + + comments.onsuccess = (event) => { + + const cursor = event.target.result; + if (cursor) { + const comment = cursor.value; + + cursor.delete(); + + resolve(comment); + return; + } + + resolve(null); + }; + + comments.onerror = (event) => { reject("Search for unimported comments failed") }; + }); +} + +async function retrieveComment() { + try { + var database = await getDatabase(); + var queue = await getDatabaseStore(database, "CommentQueue"); + return getUnregisteredCommentFromQueue(queue); + } catch (error) { + console.error(error); + } + return null; +} + function deleteDatabase(name) { return new Promise((resolve, reject) => { var deletion = indexedDB.deleteDatabase(name); @@ -109,4 +168,4 @@ async function deleteAllPipelines() { } } -export { registerPipeline, retrievePipeline, deleteAllPipelines } +export { registerPipeline, retrievePipeline, deleteAllPipelines, addComment, retrieveComment } diff --git a/public/style.css b/public/style.css index b2627fd..b4a17ce 100644 --- a/public/style.css +++ b/public/style.css @@ -52,20 +52,46 @@ button{ justify-content: center; } -.conflict-operators{ - display: flex; - flex-direction: row; - align-items: center; - justify-content: flex-end; +.operators{ + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; +} + +.navigation{ + position: absolute; + font-size: larger; + top:5%; + right:3%; + display: flex; + flex-direction: row; +} + +.notes{ + right: 5%; + cursor: pointer; +} +.notes:hover .notes-desc{ + visibility: visible; +} +.notes-desc{ + font-size: medium; + position: absolute; + width: 300px; + padding: 0px; + right: 1px; + text-align: end; + visibility: hidden; } -.tooltip { +.swc-tooltip { position: relative; border-bottom: 1px dotted black; white-space:pre-wrap; } -.tooltip .tooltiptext { +.swc-tooltip .swc-tooltiptext { visibility: hidden; background-color: #555; color: #fff; @@ -80,12 +106,12 @@ opacity: 0; transition: opacity 0.3s; border-radius: 12px; } -.tooltip .tooltiptext p{ +.swc-tooltip .swc-tooltiptext p{ font-size: large; margin: 0px; } -.tooltip .tooltiptext::after { +.swc-tooltip .swc-tooltiptext::after { content: ""; position: absolute; top: 100%; @@ -97,14 +123,14 @@ border-style: solid; border-color: #555 transparent transparent transparent; } -.tooltip:hover .tooltiptext { +.swc-tooltip:hover .swc-tooltiptext { visibility: visible; opacity: 1; } button{ border-radius: 12px; - background-image: -webkit-linear-gradient(166.66deg, rgb(34, 198, 227) 4.62%, rgb(23, 124, 207) 10.62%, rgb(116, 75, 196) 86.29%); + background-color: rgb(23, 124, 207) ; color: white; border-color: white; padding: 10px; @@ -137,7 +163,8 @@ button{ text-align: center; background-image: none; background-color: #ddd; - color:black + color:black; + padding: 0px; } @@ -164,8 +191,8 @@ button{ .dropdown { position: relative; display: inline-block; - left: 41%; - top: -15px; + left: 36%; + top: 10px; } /* Dropdown Content (Hidden by Default) */ @@ -191,4 +218,81 @@ button{ .dropdown-content a:hover {background-color: #f1f1f1} /* Show the dropdown menu (use JS to add this class to the .dropdown-content container when the user clicks on the dropdown button) */ -.show {display:block;} \ No newline at end of file +.show {display:block;} + +tr:hover .single-line-comment{ + opacity: 1; + pointer-events: auto; +} +.single-line-comment{ + position:absolute; + right: 20%; + opacity: 0; + pointer-events: none; + transition: opacity 0.2s; +} + +.single-line-comment:hover .hover-text{ + visibility: visible; +} + +.hover-text{ + visibility: hidden; + position: relative; + background-color: #555; + color: #fff; + text-align: center; + padding: 2px; + border-radius: 6px; + z-index: 1; +} +.hover-text::after { +content: ""; +position: absolute; +bottom: 100%; +left: 50%; +margin-left: -5px; +margin-right: 5px; +border-width: 5px; +border-style: solid; +border-color: transparent transparent #555 transparent; +} + +.single-line-comment-popup{ + position: absolute; + visibility: hidden; + background-color: #bababa; + padding: 5px; + border-radius: 12px; + top: 17px; + left: -250px; + z-index: 1; +} +.single-line-comment-popup::after { +content: ""; +position: absolute; +bottom: 100%; +left: 50%; +margin-left: -5px; +margin-right: 5px; +border-width: 5px; +border-style: solid; +border-color: transparent transparent #bababa transparent; +} + +input[type=text]{ + width:500px; + border-radius: 12px; +} + +#conflict{ + display: flex; + flex-direction: row; +} + +.comment{ + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; +}