github.com/zppinho/prow@v0.0.0-20240510014325-1738badeb017/cmd/deck/static/common/common.ts (about) 1 import moment from "moment"; 2 import {ProwJobState, Pull} from "../api/prow"; 3 4 // This file likes namespaces, so stick with it for now. 5 /* eslint-disable @typescript-eslint/no-namespace */ 6 7 // State enum describes different state a job can be in 8 export enum State { 9 TRIGGERED = 'triggered', 10 PENDING = 'pending', 11 SUCCESS = 'success', 12 FAILURE = 'failure', 13 ABORTED = 'aborted', 14 ERROR = 'error', 15 } 16 17 // The cell namespace exposes functions for constructing common table cells. 18 export namespace cell { 19 20 export function text(content: string): HTMLTableDataCellElement { 21 const c = document.createElement("td"); 22 c.appendChild(document.createTextNode(content)); 23 return c; 24 } 25 26 export function time(id: string, when: moment.Moment): HTMLTableDataCellElement { 27 const tid = `time-cell-${ id}`; 28 const main = document.createElement("div"); 29 const isADayOld = when.isBefore(moment().startOf('day')); 30 main.textContent = when.format(isADayOld ? 'MMM DD HH:mm:ss' : 'HH:mm:ss'); 31 main.id = tid; 32 33 const tip = tooltip.forElem(tid, document.createTextNode(when.format('MMM DD YYYY, HH:mm:ss [UTC]ZZ'))); 34 const c = document.createElement("td"); 35 c.appendChild(main); 36 c.appendChild(tip); 37 38 return c; 39 } 40 41 export function link(displayText: string, url: string): HTMLTableDataCellElement { 42 const c = document.createElement("td"); 43 const a = document.createElement("a"); 44 a.href = url; 45 a.appendChild(document.createTextNode(displayText)); 46 c.appendChild(a); 47 return c; 48 } 49 50 export function state(s: ProwJobState): HTMLTableDataCellElement { 51 const c = document.createElement("td"); 52 if (!s) { 53 c.appendChild(document.createTextNode("")); 54 return c; 55 } 56 57 let displayState = stateToAdj(s); 58 displayState = displayState[0].toUpperCase() + displayState.slice(1); 59 let displayIcon = ""; 60 switch (s) { 61 case State.TRIGGERED: 62 displayIcon = "schedule"; 63 break; 64 case State.PENDING: 65 displayIcon = "watch_later"; 66 break; 67 case State.SUCCESS: 68 displayIcon = "check_circle"; 69 break; 70 case State.FAILURE: 71 displayIcon = "error"; 72 break; 73 case State.ABORTED: 74 displayIcon = "remove_circle"; 75 break; 76 case State.ERROR: 77 displayIcon = "warning"; 78 break; 79 } 80 const stateIndicator = document.createElement("i"); 81 stateIndicator.classList.add("material-icons", "state", s); 82 stateIndicator.innerText = displayIcon; 83 c.appendChild(stateIndicator); 84 c.title = displayState; 85 86 return c; 87 } 88 89 function stateToAdj(s: ProwJobState): string { 90 switch (s) { 91 case "success": 92 return "succeeded"; 93 case "failure": 94 return "failed"; 95 default: 96 return s; 97 } 98 } 99 100 export function commitRevision(repo: string, ref: string, SHA: string, pushCommitLink: string): HTMLTableDataCellElement { 101 const c = document.createElement("td"); 102 const bl = document.createElement("a"); 103 bl.href = pushCommitLink; 104 if (!bl.href) { 105 bl.href = `/github-link?dest=${repo}/commit/${SHA}`; 106 } 107 bl.text = `${ref} (${SHA.slice(0, 7)})`; 108 c.appendChild(bl); 109 return c; 110 } 111 112 export function prRevision(repo: string, pull: Pull): HTMLTableDataCellElement { 113 const td = document.createElement("td"); 114 addPRRevision(td, repo, pull); 115 return td; 116 } 117 118 let idCounter = 0; 119 function nextID(): string { 120 idCounter++; 121 return `tipID-${ String(idCounter)}`; 122 } 123 124 export function addPRRevision(elem: Node, repo: string, pull: Pull): void { 125 elem.appendChild(document.createTextNode("#")); 126 const pl = document.createElement("a"); 127 if (pull.link) { 128 pl.href = pull.link; 129 } else { 130 pl.href = `/git-provider-link?target=pr&repo='${repo}'&number=${pull.number}`; 131 } 132 pl.text = pull.number.toString(); 133 if (pull.title) { 134 pl.id = `pr-${repo}-${pull.number}-${nextID()}`; 135 const tip = tooltip.forElem(pl.id, document.createTextNode(pull.title)); 136 pl.appendChild(tip); 137 } 138 elem.appendChild(pl); 139 if (pull.sha) { 140 elem.appendChild(document.createTextNode(" (")); 141 const cl = document.createElement("a"); 142 if (pull.commit_link) { 143 cl.href = pull.commit_link; 144 } else { 145 cl.href = `/git-provider-link?target=prcommit&repo='${repo}'&number=${pull.number}&commit=${pull.sha}`; 146 } 147 cl.text = pull.sha.slice(0, 7); 148 elem.appendChild(cl); 149 elem.appendChild(document.createTextNode(")")); 150 } 151 if (pull.author) { 152 elem.appendChild(document.createTextNode(" by ")); 153 const al = document.createElement("a"); 154 if (pull.author_link) { 155 al.href = pull.author_link; 156 } else { 157 al.href = `/git-provider-link?target=author&repo='${repo}'&author=${pull.author}`; 158 } 159 al.text = pull.author; 160 elem.appendChild(al); 161 } 162 } 163 } 164 165 export namespace tooltip { 166 export function forElem(elemID: string, tipElem: Node): HTMLElement { 167 const tip = document.createElement("div"); 168 tip.appendChild(tipElem); 169 tip.setAttribute("data-mdl-for", elemID); 170 tip.classList.add("mdl-tooltip", "mdl-tooltip--large"); 171 tip.style.whiteSpace = "normal"; 172 return tip; 173 } 174 } 175 176 export namespace icon { 177 export function create(iconString: string, tip = "", onClick?: (this: HTMLElement, ev: MouseEvent) => any): HTMLAnchorElement { 178 const i = document.createElement("i"); 179 i.classList.add("icon-button", "material-icons"); 180 i.innerHTML = iconString; 181 if (tip !== "") { 182 i.title = tip; 183 } 184 if (onClick) { 185 i.addEventListener("click", onClick); 186 } 187 188 const container = document.createElement("a"); 189 container.appendChild(i); 190 container.classList.add("mdl-button", "mdl-js-button", "mdl-button--icon"); 191 192 return container; 193 } 194 } 195 196 export namespace tidehistory { 197 export function poolIcon(org: string, repo: string, branch: string): HTMLAnchorElement { 198 const link = icon.create("timeline", "Pool History"); 199 const encodedRepo = encodeURIComponent(`${org}/${repo}`); 200 const encodedBranch = encodeURIComponent(branch); 201 link.href = `/tide-history?repo=${encodedRepo}&branch=${encodedBranch}`; 202 return link; 203 } 204 205 export function authorIcon(author: string): HTMLAnchorElement { 206 const link = icon.create("timeline", "Personal Tide History"); 207 const encodedAuthor = encodeURIComponent(author); 208 link.href = `/tide-history?author=${encodedAuthor}`; 209 return link; 210 } 211 } 212 213 export function getCookieByName(name: string): string { 214 if (!document.cookie) { 215 return ""; 216 } 217 const docCookies = decodeURIComponent(document.cookie).split(";"); 218 for (const cookie of docCookies) { 219 const c = cookie.trim(); 220 const pref = `${name }=`; 221 if (c.indexOf(pref) === 0) { 222 return c.slice(pref.length); 223 } 224 } 225 return ""; 226 } 227 228 export function showToast(text: string): void { 229 const toast = document.getElementById("toast") as SnackbarElement<HTMLDivElement>; 230 toast.MaterialSnackbar.showSnackbar({message: text}); 231 } 232 233 export function showAlert(text: string): void { 234 const toast = document.getElementById("toastAlert") as SnackbarElement<HTMLDivElement>; 235 toast.MaterialSnackbar.showSnackbar({message: text}); 236 } 237 238 // copyToClipboard is from https://stackoverflow.com/a/33928558 239 // Copies a string to the clipboard. Must be called from within an 240 // event handler such as click. May return false if it failed, but 241 // this is not always possible. Browser support for Chrome 43+, 242 // Firefox 42+, Safari 10+, Edge and IE 10+. 243 // IE: The clipboard feature may be disabled by an administrator. By 244 // default a prompt is shown the first time the clipboard is 245 // used (per session). 246 export function copyToClipboard(text: string) { 247 if (window.clipboardData && window.clipboardData.setData) { 248 // IE specific code path to prevent textarea being shown while dialog is visible. 249 return window.clipboardData.setData("Text", text); 250 } else if (document.queryCommandSupported && document.queryCommandSupported("copy")) { 251 const textarea = document.createElement("textarea"); 252 textarea.textContent = text; 253 textarea.style.position = "fixed"; // Prevent scrolling to bottom of page in MS Edge. 254 document.body.appendChild(textarea); 255 textarea.select(); 256 try { 257 return document.execCommand("copy"); // Security exception may be thrown by some browsers. 258 } catch (ex) { 259 console.warn("Copy to clipboard failed.", ex); 260 return false; 261 } finally { 262 document.body.removeChild(textarea); 263 } 264 } 265 } 266 export function formatDuration(seconds: number): string { 267 const parts: string[] = []; 268 if (seconds >= 3600) { 269 const hours = Math.floor(seconds / 3600); 270 parts.push(String(hours)); 271 parts.push('h'); 272 seconds = seconds % 3600; 273 } 274 if (seconds >= 60) { 275 const minutes = Math.floor(seconds / 60); 276 if (minutes > 0) { 277 parts.push(String(minutes)); 278 parts.push('m'); 279 seconds = seconds % 60; 280 } 281 } 282 if (seconds >= 0) { 283 parts.push(String(seconds)); 284 parts.push('s'); 285 } 286 return parts.join(''); 287 }