Zendesk Enhancements

Améliore l'interface Zendesk et ajoute des fonctionnalités supplémentaires

  1. // ==UserScript==
  2. // @name Zendesk Enhancements
  3. // @namespace http://tampermonkey.net/
  4. // @version 1.3.3
  5. // @description Améliore l'interface Zendesk et ajoute des fonctionnalités supplémentaires
  6. // @author Morgan & gann
  7. // @match https://*.zendesk.com/*
  8. // @grant GM_addStyle
  9. // @license MIT
  10. // ==/UserScript==
  11.  
  12. (function() {
  13. 'use strict';
  14.  
  15. // Définition d'un style commun pour les éléments personnalisés
  16. var customCSS = `
  17. .custom-button, .djm-task-link, .copy-conversation-button {
  18. padding: 5px 10px;
  19. background-color: limegreen;
  20. font-size: 16px;
  21. color: black;
  22. border: 1px solid transparent;
  23. border-radius: 4px;
  24. cursor: pointer;
  25. text-align: center;
  26. text-decoration: none;
  27. display: inline-block;
  28. margin: 5px;
  29. transition: background-color 0.15s ease, color 0.15s ease;
  30. &:hover {
  31. background-color: darkgreen; /* Couleur de fond au survol */
  32. color: white; /* Couleur du texte au survol */
  33. }
  34. }
  35.  
  36. .sc-1oduqug-0.jdCBDY {
  37. margin-top: 30px;
  38. }
  39.  
  40. iframe#web-messenger-container {
  41. display: none;
  42. }
  43.  
  44. .app_view.app-1019154.apps_ticket_sidebar iframe {
  45. height: 80vh!important;
  46. }
  47. /* Enhence copy conversation btn */
  48. .sc-1nvv38f-3.cjpyOe {
  49. flex: unset;
  50. }
  51. /*Enhence next conversation btn */
  52. .jGrowl-notification {
  53. top: 30px;
  54. }
  55. `;
  56.  
  57. GM_addStyle(customCSS);
  58.  
  59. // Initialisation des fonctions personnalisées
  60. setTimeout(function() {
  61. customFunction()
  62. }, 500);
  63.  
  64. function customFunction(message) {
  65. setTimeout(function() {
  66. checkUrlAndRunScriptInitializeInputLinks();
  67. checkUrlAndRunScriptTransformUrlsToLinks();
  68. }, 500);
  69. }
  70.  
  71. // Écoute les changements de navigation
  72. window.addEventListener('popstate', function() {
  73. customFunction('Chemin changé: ' + window.location.pathname);
  74. });
  75.  
  76. // Surcharge des méthodes d'historique
  77. function overrideHistoryMethod(methodName) {
  78. var originalMethod = history[methodName];
  79. history[methodName] = function(state) {
  80. if (typeof history['on' + methodName] == "function") {
  81. history['on' + methodName]({state: state});
  82. }
  83. customFunction('Chemin changé par ' + methodName + ': ' + window.location.pathname);
  84. return originalMethod.apply(history, arguments);
  85. };
  86. }
  87.  
  88. overrideHistoryMethod('pushState');
  89. overrideHistoryMethod('replaceState');
  90.  
  91. window.history.onpushstate = function(e) {
  92. window.dispatchEvent(new CustomEvent('pushstate', e));
  93. };
  94. window.history.onreplacestate = function(e) {
  95. window.dispatchEvent(new CustomEvent('replacestate', e));
  96. };
  97.  
  98. // Événements sur les en-têtes de tableau
  99. var theads = document.querySelectorAll('thead');
  100. theads.forEach(function(thead) {
  101. thead.addEventListener('click', function(event) {
  102. customFunction('Un thead a été cliqué' + event.target);
  103. });
  104. });
  105.  
  106. function transformUrlsToLinks() {
  107. const cells = document.querySelectorAll('.beWvMU');
  108. const urlRegex = /(https?:\/\/[^\s]+)/g;
  109.  
  110. if (cells.length > 0) {
  111. cells.forEach((cell, index) => {
  112. const textContent = cell.textContent;
  113.  
  114. if (urlRegex.test(textContent)) {
  115. const link = document.createElement('a');
  116. link.setAttribute('href', textContent.match(urlRegex)[0]);
  117. link.textContent = textContent.match(urlRegex)[0];
  118. link.classList.add('custom-button');
  119. cell.textContent = '';
  120. cell.appendChild(link);
  121. }
  122. });
  123. }
  124. }
  125.  
  126. function checkUrlAndRunScriptTransformUrlsToLinks() {
  127. if (window.location.pathname.startsWith('/agent/filters/')) {
  128. transformUrlsToLinks();
  129. }
  130. }
  131.  
  132. function initializeInputLinks() {
  133. var inputElements = document.querySelectorAll('.custom_field_14504424601628 input');
  134.  
  135. inputElements.forEach(function(inputElement) {
  136. var existingLink = inputElement.parentNode.parentNode.querySelector('.djm-task-link');
  137. if (existingLink) {
  138. existingLink.remove();
  139. }
  140.  
  141. var linkElement = document.createElement('a');
  142. linkElement.textContent = 'Tâche';
  143. linkElement.style.display = 'none';
  144. linkElement.classList.add('djm-task-link');
  145. inputElement.parentNode.parentNode.insertBefore(linkElement, inputElement.nextSibling);
  146.  
  147. checkForUrl(inputElement, linkElement);
  148.  
  149. inputElement.addEventListener('input', function() {
  150. console.log('Événement input détecté.');
  151. checkForUrl(inputElement, linkElement);
  152. });
  153. });
  154. }
  155.  
  156. function checkForUrl(inputElement, linkElement) {
  157. if (inputElement.value.match(/(https?:\/\/[^\s]+)/g)) {
  158. linkElement.href = inputElement.value;
  159. linkElement.style.display = 'inline-block';
  160. } else {
  161. linkElement.style.display = 'none';
  162. }
  163. }
  164.  
  165. function checkUrlAndRunScriptInitializeInputLinks() {
  166. if (window.location.pathname.startsWith('/agent/tickets/')) {
  167. initializeInputLinks();
  168. }
  169. }
  170.  
  171. // Fonction pour fermer tous les onglets inactifs
  172. function closeInactiveTabs() {
  173. const closeButtons = [...document.querySelectorAll('div[role="tab"][data-selected="false"] button[data-test-id="close-button"]')];
  174. closeButtons.forEach(btn => btn.click());
  175. }
  176.  
  177. // Fonction pour ajouter le bouton "Close All"
  178. function addCloseAllButton() {
  179. const toolbar = document.querySelector('div[data-test-id="header-tablist"] > div.sc-19uji9v-0');
  180. if (toolbar && !document.getElementById('close-all-button')) {
  181. const closeButton = document.createElement('button');
  182. closeButton.id = 'close-all-button';
  183. closeButton.textContent = 'X Close all';
  184. closeButton.className = 'custom-button';
  185. closeButton.addEventListener('click', closeInactiveTabs);
  186.  
  187. toolbar.appendChild(closeButton);
  188. }
  189. }
  190.  
  191. // Ajout du bouton "Close All" au chargement de la page
  192. window.addEventListener('load', addCloseAllButton);
  193.  
  194. // Observer les changements dans le DOM pour charger dynamiquement le bouton
  195. const observer = new MutationObserver(addCloseAllButton);
  196. observer.observe(document.body, { childList: true, subtree: true });
  197.  
  198.  
  199.  
  200. function addCopyButtons() {
  201. // Get all 'ember-view workspace has-play' elements
  202. const workspaces = document.querySelectorAll('.ember-view.workspace');
  203.  
  204. workspaces.forEach((workspace, index) => {
  205. // Find the 'sc-1nvv38f-3 cjpyOe' element within each workspace
  206. const headerElement = workspace.querySelector('.sc-1nvv38f-3.cjpyOe');
  207.  
  208. if (headerElement) {
  209. // Check if the button already exists to avoid duplicates
  210. if (!headerElement.querySelector('.copy-conversation-button')) {
  211. // Create a new button
  212. const button = document.createElement('button');
  213. button.innerText = 'Copy Conversation';
  214. button.style.marginLeft = '10px';
  215. button.classList.add('copy-conversation-button'); // Add a class for easy selection
  216.  
  217. // Add the click event to copy conversation text
  218. button.addEventListener('click', () => {
  219. // Find the conversation text container
  220. const conversationContainer = workspace.querySelector('.sc-175iuw8-0.ecaNtR.conversation-polaris.polaris-react-component.rich_text');
  221.  
  222. if (conversationContainer) {
  223. const textToCopy = conversationContainer.innerText;
  224.  
  225. // Append "[Ma réponse au client]" to the text
  226. const modifiedText = textToCopy + '\n[Ma réponse au client]\n';
  227.  
  228. // Copy the modified text to clipboard
  229. copyToClipboard(modifiedText);
  230. }
  231. });
  232.  
  233. // Append the button to the header element
  234. headerElement.appendChild(button);
  235. }
  236. }
  237. });
  238. }
  239.  
  240. // Function to copy text to clipboard
  241. function copyToClipboard(text) {
  242. navigator.clipboard.writeText(text).catch(err => {
  243. console.error('Failed to copy text: ', err);
  244. });
  245. }
  246.  
  247. // Run the function every second
  248. setInterval(addCopyButtons, 1000);
  249.  
  250.  
  251.  
  252. })();