Keka Log Duration by Jp

try to take over the world

目前为 2024-03-01 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name Keka Log Duration by Jp
  3. // @namespace http://tampermonkey.net/
  4. // @version 1.2
  5. // @description try to take over the world
  6. // @author You
  7. // @match https://ezeetechnosys.keka.com/
  8. // @icon https://www.google.com/s2/favicons?sz=64&domain=keka.com
  9. // @grant none
  10. // @license MIT
  11. // ==/UserScript==
  12.  
  13. (function () {
  14. 'use strict';
  15. const channel = new BroadcastChannel('keka-duration');
  16. let fistDuration;
  17. let secondDuration;
  18. let thirdDuration;
  19. let fourthDuration;
  20. let fifthDuration;
  21. function parseTime(timeStr) {
  22. const parts = timeStr.split(':');
  23. return parseInt(parts[0]) * 3600000 + parseInt(parts[1]) * 60000 + parseInt(parts[2]) * 1000;
  24. }
  25. let lastLogIndex=0;
  26.  
  27. function calculateTotalDuration(duration1Str, duration2Str, duration3Str, duration4Str, duration5Str) {
  28. // Initialize total hours and minutes
  29. let totalHours = 0;
  30. let totalMinutes = 0;
  31.  
  32. // Array of all duration strings
  33. const durations = [duration1Str, duration2Str, duration3Str, duration4Str, duration5Str];
  34.  
  35. // Loop through each duration string
  36. durations.forEach(durationStr => {
  37. if (durationStr) { // Check if the duration string is not empty
  38. const [hours, minutes] = durationStr.match(/\d+/g).map(Number);
  39. totalHours += hours;
  40. totalMinutes += minutes;
  41. }
  42. });
  43.  
  44. // Add overflow from minutes to hours
  45. totalHours += Math.floor(totalMinutes / 60);
  46. totalMinutes = totalMinutes % 60;
  47.  
  48. // Format the total duration string
  49. const totalDurationStr = `${totalHours} Hr ${totalMinutes} Min`;
  50.  
  51. return totalDurationStr;
  52. }
  53.  
  54.  
  55. function calculateDuration(startTimeSpan, endTimeSpan) {
  56. const startTimeStr = startTimeSpan.textContent.trim();
  57. const endTimeStr = endTimeSpan.textContent.trim();
  58.  
  59. // If end time is empty, use current time
  60. const endTime = endTimeStr != "MISSING" ? endTimeStr : new Date().toLocaleTimeString('en-US', { hour12: true });
  61. console.log("TIMES", startTimeStr, endTime);
  62. // Parse time strings into Date objects
  63. const startTime = new Date(`2023-12-27 ${startTimeStr}`); // Assuming today's date
  64. const endTimeDate = new Date(`2023-12-27 ${endTime}`);
  65.  
  66. // Calculate duration in milliseconds
  67. const durationMillis = endTimeDate.getTime() - startTime.getTime();
  68.  
  69. // Convert milliseconds to hours and minutes
  70. const durationHours = Math.floor(durationMillis / (3600 * 1000));
  71. const durationMinutes = Math.floor((durationMillis % (3600 * 1000)) / (60 * 1000));
  72.  
  73. // Formatted duration string
  74. const durationStr = `${durationHours} Hr ${durationMinutes} Min`;
  75.  
  76. return durationStr;
  77. }
  78.  
  79. function watchForModals() {
  80. const modals = document.querySelectorAll('body > modal-container > div.modal-dialog.small-modal > div > attendance-adjustment-request > div.modal-body > form > div.mt-20 > div > div:nth-child(2) > div');
  81. console.log("Modals", modals)
  82.  
  83. modals.forEach(modal => {
  84.  
  85. const modalsArray = Array.from(modals);
  86. const dayIndex = modalsArray.indexOf(modal) + 1; // Determine the day index (1, 2, or 3)
  87.  
  88. const displaySpanXPath = `/html/body/modal-container/div[2]/div/attendance-adjustment-request/div[2]/form/div[2]/div/div[2]/div/div/div/div[1]/span[1]`;
  89. const displaySpan = document.evaluate(
  90. displaySpanXPath,
  91. modal,
  92. null,
  93. XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,
  94. null
  95. ).snapshotItem(0);
  96.  
  97. const startTimeSpan = document.evaluate(
  98. '/html/body/modal-container/div[2]/div/attendance-adjustment-request/div[2]/form/div[2]/div/div[2]/div/div/div[1]/div[1]/div[2]/span[2]',
  99. modal,
  100. null,
  101. XPathResult.FIRST_ORDERED_NODE_TYPE,
  102. null
  103. ).singleNodeValue;
  104.  
  105. const endTimeSpan = document.evaluate(
  106. '/html/body/modal-container/div[2]/div/attendance-adjustment-request/div[2]/form/div[2]/div/div[2]/div/div/div[1]/div[1]/div[3]/span[2]',
  107. modal,
  108. null,
  109. XPathResult.FIRST_ORDERED_NODE_TYPE,
  110. null
  111. ).singleNodeValue;
  112.  
  113. const startTimeSpan1 = document.evaluate(
  114. '/html/body/modal-container/div[2]/div/attendance-adjustment-request/div[2]/form/div[2]/div/div[2]/div/div/div[2]/div[1]/div[2]/span[2]',
  115. modal,
  116. null,
  117. XPathResult.FIRST_ORDERED_NODE_TYPE,
  118. null
  119. ).singleNodeValue;
  120.  
  121. const endTimeSpan1 = document.evaluate(
  122. '/html/body/modal-container/div[2]/div/attendance-adjustment-request/div[2]/form/div[2]/div/div[2]/div/div/div[2]/div[1]/div[3]/span[2]',
  123. modal,
  124. null,
  125. XPathResult.FIRST_ORDERED_NODE_TYPE,
  126. null
  127. ).singleNodeValue;
  128.  
  129. const startTimeSpan2 = document.evaluate(
  130. '/html/body/modal-container/div[2]/div/attendance-adjustment-request/div[2]/form/div[2]/div/div[2]/div/div/div[3]/div[1]/div[2]/span[2]',
  131. modal,
  132. null,
  133. XPathResult.FIRST_ORDERED_NODE_TYPE,
  134. null
  135. ).singleNodeValue;
  136.  
  137. const endTimeSpan2 = document.evaluate(
  138. '/html/body/modal-container/div[2]/div/attendance-adjustment-request/div[2]/form/div[2]/div/div[2]/div/div/div[3]/div[1]/div[3]/span[2]',
  139. modal,
  140. null,
  141. XPathResult.FIRST_ORDERED_NODE_TYPE,
  142. null
  143. ).singleNodeValue;
  144.  
  145. const startTimeSpan3 = document.evaluate(
  146. '/html/body/modal-container/div[2]/div/attendance-adjustment-request/div[2]/form/div[2]/div/div[2]/div/div/div[4]/div[1]/div[2]/span[2]',
  147. modal,
  148. null,
  149. XPathResult.FIRST_ORDERED_NODE_TYPE,
  150. null
  151. ).singleNodeValue;
  152.  
  153. const endTimeSpan3 = document.evaluate(
  154. '/html/body/modal-container/div[2]/div/attendance-adjustment-request/div[2]/form/div[2]/div/div[2]/div/div/div[4]/div[1]/div[3]/span[2]',
  155. modal,
  156. null,
  157. XPathResult.FIRST_ORDERED_NODE_TYPE,
  158. null
  159. ).singleNodeValue;
  160.  
  161. const startTimeSpan4 = document.evaluate(
  162. '/html/body/modal-container/div[2]/div/attendance-adjustment-request/div[2]/form/div[2]/div/div[2]/div/div/div[5]/div[1]/div[2]/span[2]',
  163. modal,
  164. null,
  165. XPathResult.FIRST_ORDERED_NODE_TYPE,
  166. null
  167. ).singleNodeValue;
  168.  
  169. const endTimeSpan4 = document.evaluate(
  170. '/html/body/modal-container/div[2]/div/attendance-adjustment-request/div[2]/form/div[2]/div/div[2]/div/div/div[5]/div[1]/div[3]/span[2]',
  171. modal,
  172. null,
  173. XPathResult.FIRST_ORDERED_NODE_TYPE,
  174. null
  175. ).singleNodeValue;
  176.  
  177.  
  178.  
  179.  
  180.  
  181. if (startTimeSpan && endTimeSpan) {
  182. const durationStr = calculateDuration(startTimeSpan, endTimeSpan);
  183. fistDuration = durationStr
  184. const durationDisplaySpan = document.querySelector('.d-flex.align-items-center.ml-10');
  185.  
  186. window.postMessage({ type: 'duration', value: durationStr }, '*');
  187. console.log("POSTED-DURATION")
  188. // Check for existing duration span:
  189. const existingDurationSpan = durationDisplaySpan.querySelector('.duration-span-1');
  190. if (!existingDurationSpan) {
  191. // Create span only if it doesn't exist
  192. const durationSpan = document.createElement('span');
  193. durationSpan.classList.add('duration-span-1'); // Add class for identification
  194. durationSpan.textContent = durationStr;
  195. durationSpan.style.fontWeight = 'bold';
  196. durationSpan.style.paddingLeft = '17px';
  197. durationSpan.style.color = 'darkred';
  198. durationDisplaySpan.insertAdjacentElement('afterend', durationSpan);
  199. } else {
  200. console.log("Duration span already exists, skipping creation.");
  201. }
  202.  
  203. var parentDivs = document.querySelectorAll('div.mb-30.ng-untouched.ng-pristine.ng-valid');
  204. if (parentDivs.length > 1) {
  205. // Targeting the second div as per your requirement
  206. var targetDiv = parentDivs[1];
  207. var pElement = targetDiv.querySelector('p.text-large'); // Assuming the span is the one you've added
  208. var spanElement = document.createElement('span');
  209. // Texts array for the morphing effect
  210. const texts = ["Log Duration", " By @Jp"];
  211. let textIndex = 0;
  212.  
  213. // Function to update the text with a morphing effect
  214. function updateText() {
  215. const nextText = texts[textIndex % texts.length];
  216. textIndex++;
  217.  
  218. // Applying a fade-out effect
  219. spanElement.style.opacity = '0';
  220. spanElement.style.transition = 'opacity 0.5s ease-out';
  221.  
  222. // After fade-out, change the text and fade it back in
  223. setTimeout(() => {
  224. spanElement.textContent = nextText;
  225.  
  226. // Applying blur during transition
  227. spanElement.style.filter = 'blur(2px)';
  228. spanElement.style.opacity = '1';
  229. spanElement.style.transition = 'opacity 0.5s ease-in, filter 0.5s ease';
  230.  
  231. // Removing blur after a moment
  232. setTimeout(() => {
  233. spanElement.style.filter = 'none';
  234. }, 500); // Match the transition time
  235. }, 500); // Wait for the fade-out to complete
  236. }
  237.  
  238. // Starting the text update process
  239. spanElement.style.fontWeight = 'bold';
  240. spanElement.style.color = 'rgb(0, 123, 255)';
  241. spanElement.style.padding = '5px';
  242. spanElement.style.borderRadius = '5px';
  243. spanElement.style.fontSize = '16px';
  244. spanElement.style.marginLeft = '262px';
  245. spanElement.textContent = texts[textIndex % texts.length];
  246. textIndex++;
  247. pElement.appendChild(spanElement);
  248. setInterval(updateText, 2000);
  249. }
  250. }
  251.  
  252. if (startTimeSpan1 && endTimeSpan1) {
  253. lastLogIndex = 1;
  254. console.log("11111111--11111", startTimeSpan1, endTimeSpan1)
  255. const durationStr = calculateDuration(startTimeSpan1, endTimeSpan1);
  256. secondDuration = durationStr;
  257. const durationDisplaySpan = document.querySelector('body > modal-container > div.modal-dialog.small-modal > div > attendance-adjustment-request > div.modal-body > form > div.mt-20 > div > div:nth-child(2) > div > div > div:nth-child(2) > div.d-flex.align-items-center.ng-untouched.ng-pristine.ng-valid > div.d-flex.align-items-center.ml-10');
  258. console.log("durationDisplaySpan", durationDisplaySpan)
  259.  
  260. // Check for existing duration span:
  261. const existingDurationSpan = durationDisplaySpan.querySelector('.duration-span-2');
  262. if (!existingDurationSpan) {
  263. // Create span only if it doesn't exist
  264. const durationSpan = document.createElement('span');
  265. durationSpan.classList.add('duration-span-2'); // Add class for identification
  266. durationSpan.textContent = durationStr;
  267. durationSpan.style.fontWeight = 'bold';
  268. durationSpan.style.paddingLeft = '17px';
  269. durationSpan.style.color = 'darkred';
  270. durationDisplaySpan.insertAdjacentElement('afterend', durationSpan);
  271. } else {
  272. console.log("Duration span already exists, skipping creation.");
  273. }
  274.  
  275. }
  276. if (startTimeSpan2 && endTimeSpan2) {
  277. lastLogIndex = 2;
  278. console.log("11111111--11111", startTimeSpan2, endTimeSpan2)
  279. const durationStr = calculateDuration(startTimeSpan2, endTimeSpan2);
  280. thirdDuration = durationStr;
  281. const durationDisplaySpan = document.querySelector('body > modal-container > div.modal-dialog.small-modal > div > attendance-adjustment-request > div.modal-body > form > div.mt-20 > div > div:nth-child(2) > div > div > div:nth-child(3) > div.d-flex.align-items-center.ng-untouched.ng-pristine.ng-valid > div.d-flex.align-items-center.ml-10');
  282. console.log("durationDisplaySpan", durationDisplaySpan)
  283.  
  284. // Check for existing duration span:
  285. const existingDurationSpan = durationDisplaySpan.querySelector('.duration-span-3');
  286. if (!existingDurationSpan) {
  287. // Create span only if it doesn't exist
  288. const durationSpan = document.createElement('span');
  289. durationSpan.classList.add('duration-span-3'); // Add class for identification
  290. durationSpan.textContent = durationStr;
  291. durationSpan.style.fontWeight = 'bold';
  292. durationSpan.style.paddingLeft = '17px';
  293. durationSpan.style.color = 'darkred';
  294. durationDisplaySpan.insertAdjacentElement('afterend', durationSpan);
  295. } else {
  296. console.log("Duration span already exists, skipping creation.");
  297. }
  298.  
  299. }
  300. if (startTimeSpan3 && endTimeSpan3) {
  301. lastLogIndex = 3;
  302. console.log("11111111--11111", startTimeSpan3, endTimeSpan3)
  303. const durationStr = calculateDuration(startTimeSpan3, endTimeSpan3);
  304. fourthDuration = durationStr;
  305. const durationDisplaySpan = document.querySelector('body > modal-container > div.modal-dialog.small-modal > div > attendance-adjustment-request > div.modal-body > form > div.mt-20 > div > div:nth-child(2) > div > div > div:nth-child(3) > div.d-flex.align-items-center.ng-untouched.ng-pristine.ng-valid > div.d-flex.align-items-center.ml-10');
  306. console.log("durationDisplaySpan", durationDisplaySpan)
  307.  
  308. // Check for existing duration span:
  309. const existingDurationSpan = durationDisplaySpan.querySelector('.duration-span-3');
  310. if (!existingDurationSpan) {
  311. // Create span only if it doesn't exist
  312. const durationSpan = document.createElement('span');
  313. durationSpan.classList.add('duration-span-3'); // Add class for identification
  314. durationSpan.textContent = durationStr;
  315. durationSpan.style.fontWeight = 'bold';
  316. durationSpan.style.paddingLeft = '17px';
  317. durationSpan.style.color = 'darkred';
  318. durationDisplaySpan.insertAdjacentElement('afterend', durationSpan);
  319. } else {
  320. console.log("Duration span already exists, skipping creation.");
  321. }
  322.  
  323. }
  324. if (startTimeSpan4 && endTimeSpan4) {
  325. lastLogIndex = 4;
  326. console.log("11111111--11111", startTimeSpan4, endTimeSpan4)
  327. const durationStr = calculateDuration(startTimeSpan4, endTimeSpan4);
  328. fifthDuration = durationStr;
  329. const durationDisplaySpan = document.querySelector('body > modal-container > div.modal-dialog.small-modal > div > attendance-adjustment-request > div.modal-body > form > div.mt-20 > div > div:nth-child(2) > div > div > div:nth-child(4) > div.d-flex.align-items-center.ng-untouched.ng-pristine.ng-valid > div.d-flex.align-items-center.ml-10');
  330. console.log("durationDisplaySpan", durationDisplaySpan)
  331.  
  332. // Check for existing duration span:
  333. const existingDurationSpan = durationDisplaySpan.querySelector('.duration-span-4');
  334. if (!existingDurationSpan) {
  335. // Create span only if it doesn't exist
  336. const durationSpan = document.createElement('span');
  337. durationSpan.classList.add('duration-span-4'); // Add class for identification
  338. durationSpan.textContent = durationStr;
  339. durationSpan.style.fontWeight = 'bold';
  340. durationSpan.style.paddingLeft = '17px';
  341. durationSpan.style.color = 'darkred';
  342. durationDisplaySpan.insertAdjacentElement('afterend', durationSpan);
  343. } else {
  344. console.log("Duration span already exists, skipping creation.");
  345. }
  346.  
  347. }
  348. if(lastLogIndex) {
  349.  
  350. const totaldurationDisplaySpan = document.querySelector(`body > modal-container > div.modal-dialog.small-modal > div > attendance-adjustment-request > div.modal-body > form > div.mt-20 > div > div:nth-child(2) > div > div > div:nth-child(${lastLogIndex+1}) > div.d-flex.align-items-center.ng-untouched.ng-pristine.ng-valid`);
  351. console.log(fistDuration, secondDuration, "#################")
  352. const total = calculateTotalDuration(fistDuration, secondDuration,thirdDuration,fourthDuration,fifthDuration);
  353. console.log("total", total)
  354.  
  355. // Check for existing total duration span:
  356. const existingTotalDurationSpan = totaldurationDisplaySpan.querySelector('.total-duration-span');
  357. if (!existingTotalDurationSpan) {
  358. // Create span only if it doesn't exist
  359. const TotaldurationSpan = document.createElement('span'); // Added closing parenthesis
  360. TotaldurationSpan.classList.add('total-duration-span'); // Add class for identification
  361. TotaldurationSpan.textContent = total;
  362. TotaldurationSpan.style.fontWeight = 'bold';
  363. // TotaldurationSpan.style.paddingLeft = '17px';
  364. TotaldurationSpan.style.color = '#0600ff';
  365. TotaldurationSpan.style.float = 'right';
  366. TotaldurationSpan.style.padding = '5px';
  367. TotaldurationSpan.style.background = 'lightblue';
  368. TotaldurationSpan.style.marginRight = '150px';
  369. TotaldurationSpan.style.borderRadius = '5px';
  370. TotaldurationSpan.style.borderTop = '3px solid #888'
  371. totaldurationDisplaySpan.insertAdjacentElement('afterend', TotaldurationSpan);
  372.  
  373. }
  374. }
  375.  
  376.  
  377.  
  378. })
  379. };
  380.  
  381.  
  382.  
  383. const observer = new MutationObserver(mutations => {
  384. console.log("Observing.....")
  385. const modalTriggerElements = document.querySelectorAll('employee-attendance-list-view .attendance-logs-row');
  386. modalTriggerElements.forEach(element => {
  387. element.addEventListener('click', watchForModals);
  388. });
  389. });
  390.  
  391. observer.observe(document.body, { childList: true, subtree: false });
  392.  
  393. })(); // Closed parenthesis for the immediately invoked function expression