Keka Log Duration by Jp

try to take over the world

  1. // ==UserScript==
  2. // @name Keka Log Duration by Jp
  3. // @namespace http://tampermonkey.net/
  4. // @version 1.7
  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.  
  16. function calculateTotalDuration(durationsArray) {
  17. let totalHours = 0;
  18. let totalMinutes = 0;
  19.  
  20. durationsArray.forEach(durationStr => {
  21. if (durationStr) {
  22. const [hours, minutes] = durationStr.match(/\d+/g).map(Number);
  23. totalHours += hours;
  24. totalMinutes += minutes;
  25. }
  26. });
  27.  
  28. // Add overflow from minutes to hours
  29. totalHours += Math.floor(totalMinutes / 60);
  30. totalMinutes = totalMinutes % 60;
  31.  
  32. // Format the total duration string
  33. const totalDurationStr = `${totalHours} Hr ${totalMinutes} Min`;
  34.  
  35. return totalDurationStr;
  36. }
  37.  
  38. function calculateDuration(startTimeSpan, endTimeSpan) {
  39. const startTimeStr = startTimeSpan.textContent.trim();
  40. const endTimeStr = endTimeSpan.textContent.trim();
  41.  
  42. // If end time is empty, use current time
  43. const endTime = endTimeStr !== "MISSING" ? endTimeStr : new Date().toLocaleTimeString('en-US', { hour12: true });
  44.  
  45. // Parse time strings into Date objects
  46. const startTime = new Date(`2023-12-27 ${startTimeStr}`); // Assuming today's date
  47. const endTimeDate = new Date(`2023-12-27 ${endTime}`);
  48.  
  49. // Calculate duration in milliseconds
  50. const durationMillis = endTimeDate.getTime() - startTime.getTime();
  51.  
  52. // Convert milliseconds to hours and minutes
  53. const durationHours = Math.floor(durationMillis / (3600 * 1000));
  54. const durationMinutes = Math.floor((durationMillis % (3600 * 1000)) / (60 * 1000));
  55.  
  56. // Formatted duration string
  57. const durationStr = `${durationHours} Hr ${durationMinutes} Min`;
  58.  
  59. return durationStr;
  60. }
  61.  
  62. function watchForModals() {
  63. 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');
  64. console.log("Modals", modals);
  65.  
  66. modals.forEach(modal => {
  67. const durations = [];
  68. let N = 1;
  69. let lastDurationDisplaySpan = null;
  70.  
  71. while (true) {
  72. // Construct XPath expressions
  73. const startTimeSpanXPath = `/html/body/modal-container/div[2]/div/attendance-adjustment-request/div[2]/form/div[2]/div/div[2]/div/div/div[${N}]/div[1]/div[2]/span[2]`;
  74. const endTimeSpanXPath = `/html/body/modal-container/div[2]/div/attendance-adjustment-request/div[2]/form/div[2]/div/div[2]/div/div/div[${N}]/div[1]/div[3]/span[2]`;
  75.  
  76. // Fetch elements
  77. const startTimeSpan = document.evaluate(startTimeSpanXPath, modal, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
  78. const endTimeSpan = document.evaluate(endTimeSpanXPath, modal, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
  79.  
  80. if (!startTimeSpan || !endTimeSpan) {
  81. // No more durations
  82. break;
  83. }
  84.  
  85. // Calculate duration
  86. const durationStr = calculateDuration(startTimeSpan, endTimeSpan);
  87.  
  88. // Store durationStr
  89. durations.push(durationStr);
  90.  
  91. // Create or update the duration display span
  92. 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(${N}) > div.d-flex.align-items-center.ng-untouched.ng-pristine.ng-valid > div.d-flex.align-items-center.ml-10`);
  93.  
  94. if (durationDisplaySpan) {
  95. // Check if duration span already exists
  96. let existingDurationSpan = durationDisplaySpan.querySelector(`.duration-span-${N}`);
  97. if (!existingDurationSpan) {
  98. // Create duration span
  99. existingDurationSpan = document.createElement('span');
  100. existingDurationSpan.classList.add(`duration-span-${N}`);
  101. existingDurationSpan.style.fontWeight = 'bold';
  102. existingDurationSpan.style.paddingLeft = '17px';
  103. existingDurationSpan.style.color = 'darkred';
  104. durationDisplaySpan.insertAdjacentElement('afterend', existingDurationSpan);
  105. }
  106. existingDurationSpan.textContent = durationStr;
  107.  
  108. // Keep track of the last valid durationDisplaySpan
  109. lastDurationDisplaySpan = durationDisplaySpan;
  110. } else {
  111. console.warn(`durationDisplaySpan not found for N = ${N}`);
  112. }
  113.  
  114. N++;
  115. }
  116.  
  117. // Decrement N to point to the last valid index
  118. N--;
  119.  
  120. // Only display total duration if more than one duration exists
  121. if (durations.length > 1 && lastDurationDisplaySpan) {
  122. const totalDurationStr = calculateTotalDuration(durations);
  123.  
  124. // Display total duration
  125. const totalDurationDisplaySpanSelector = `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(${N + 1}) > div.d-flex.align-items-center.ng-untouched.ng-pristine.ng-valid`;
  126. let totalDurationDisplaySpan = document.querySelector(totalDurationDisplaySpanSelector);
  127.  
  128. // If the element doesn't exist, create it
  129. if (!totalDurationDisplaySpan) {
  130. totalDurationDisplaySpan = document.createElement('div');
  131. totalDurationDisplaySpan.classList.add('d-flex', 'align-items-center', 'ng-untouched', 'ng-pristine', 'ng-valid');
  132. const parentElementSelector = `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`;
  133. const parentElement = document.querySelector(parentElementSelector);
  134. if (parentElement) {
  135. parentElement.appendChild(totalDurationDisplaySpan);
  136. } else {
  137. console.warn('Parent element for total duration not found.');
  138. }
  139. }
  140.  
  141. // Check for existing total duration span:
  142. let existingTotalDurationSpan = totalDurationDisplaySpan.querySelector('.total-duration-span');
  143. if (!existingTotalDurationSpan) {
  144. existingTotalDurationSpan = document.createElement('span');
  145. existingTotalDurationSpan.classList.add('total-duration-span');
  146. existingTotalDurationSpan.style.fontWeight = 'bold';
  147. existingTotalDurationSpan.style.color = '#0600ff';
  148. existingTotalDurationSpan.style.float = 'right';
  149. existingTotalDurationSpan.style.padding = '5px';
  150. existingTotalDurationSpan.style.marginLeft = '375px'; // Adjusted as per your script
  151. existingTotalDurationSpan.style.background = 'lightblue';
  152. existingTotalDurationSpan.style.borderRadius = '5px';
  153. existingTotalDurationSpan.style.borderTop = '3px solid #888';
  154.  
  155. totalDurationDisplaySpan.appendChild(existingTotalDurationSpan);
  156. }
  157. existingTotalDurationSpan.textContent = totalDurationStr;
  158. } else {
  159. console.warn('No durations found or only one duration present. Total duration will not be displayed.');
  160. }
  161.  
  162. // Add morphing text effect
  163. addMorphingTextEffect();
  164. });
  165. }
  166.  
  167. function addMorphingTextEffect() {
  168. const parentDivs = document.querySelectorAll('div.mb-30.ng-untouched.ng-pristine.ng-valid');
  169. if (parentDivs.length > 1) {
  170. // Targeting the second div as per your requirement
  171. const targetDiv = parentDivs[1];
  172. const pElement = targetDiv.querySelector('p.text-large'); // Assuming the span is the one you've added
  173. if (!pElement) {
  174. console.warn('p.text-large element not found.');
  175. return;
  176. }
  177. let spanElement = pElement.querySelector('.morphing-text-span');
  178.  
  179. if (!spanElement) {
  180. spanElement = document.createElement('span');
  181. spanElement.classList.add('morphing-text-span');
  182. // Starting the text update process
  183. spanElement.style.fontWeight = 'bold';
  184. spanElement.style.color = 'rgb(0, 123, 255)';
  185. spanElement.style.padding = '5px';
  186. spanElement.style.borderRadius = '5px';
  187. spanElement.style.fontSize = '16px';
  188. spanElement.style.marginLeft = '262px';
  189. pElement.appendChild(spanElement);
  190. }
  191.  
  192. // Texts array for the morphing effect
  193. const texts = ["Log Duration", " By @Jaydeep😶"];
  194. let textIndex = 0;
  195.  
  196. // Function to update the text with a morphing effect
  197. function updateText() {
  198. const nextText = texts[textIndex % texts.length];
  199. textIndex++;
  200.  
  201. // Applying a fade-out effect
  202. spanElement.style.opacity = '0';
  203. spanElement.style.transition = 'opacity 0.5s ease-out';
  204.  
  205. // After fade-out, change the text and fade it back in
  206. setTimeout(() => {
  207. spanElement.textContent = nextText;
  208.  
  209. // Applying blur during transition
  210. spanElement.style.filter = 'blur(2px)';
  211. spanElement.style.opacity = '1';
  212. spanElement.style.transition = 'opacity 0.5s ease-in, filter 0.5s ease';
  213.  
  214. // Removing blur after a moment
  215. setTimeout(() => {
  216. spanElement.style.filter = 'none';
  217. }, 500); // Match the transition time
  218. }, 500); // Wait for the fade-out to complete
  219. }
  220.  
  221. // Initialize the text content
  222. spanElement.textContent = texts[textIndex % texts.length];
  223. textIndex++;
  224.  
  225. // Start the interval for morphing text
  226. setInterval(updateText, 2000);
  227. }
  228. }
  229.  
  230. const observer = new MutationObserver(mutations => {
  231. console.log("Observing.....");
  232. const modalTriggerElements = document.querySelectorAll('employee-attendance-list-view .attendance-logs-row');
  233. modalTriggerElements.forEach(element => {
  234. element.addEventListener('click', watchForModals);
  235. });
  236. });
  237.  
  238. observer.observe(document.body, { childList: true, subtree: false });
  239.  
  240. })();