Zendesk Enhancements

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

当前为 2025-03-10 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name Zendesk Enhancements
  3. // @namespace http://tampermonkey.net/
  4. // @version 1.3.1
  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 copi conversation btn*/
  48. .sc-1nvv38f-3.cjpyOe {
  49. flex: unset;
  50. }
  51. `;
  52.  
  53. GM_addStyle(customCSS);
  54.  
  55. // Initialisation des fonctions personnalisées
  56. setTimeout(function() {
  57. customFunction()
  58. }, 500);
  59.  
  60. function customFunction(message) {
  61. setTimeout(function() {
  62. checkUrlAndRunScriptInitializeInputLinks();
  63. checkUrlAndRunScriptTransformUrlsToLinks();
  64. }, 500);
  65. }
  66.  
  67. // Écoute les changements de navigation
  68. window.addEventListener('popstate', function() {
  69. customFunction('Chemin changé: ' + window.location.pathname);
  70. });
  71.  
  72. // Surcharge des méthodes d'historique
  73. function overrideHistoryMethod(methodName) {
  74. var originalMethod = history[methodName];
  75. history[methodName] = function(state) {
  76. if (typeof history['on' + methodName] == "function") {
  77. history['on' + methodName]({state: state});
  78. }
  79. customFunction('Chemin changé par ' + methodName + ': ' + window.location.pathname);
  80. return originalMethod.apply(history, arguments);
  81. };
  82. }
  83.  
  84. overrideHistoryMethod('pushState');
  85. overrideHistoryMethod('replaceState');
  86.  
  87. window.history.onpushstate = function(e) {
  88. window.dispatchEvent(new CustomEvent('pushstate', e));
  89. };
  90. window.history.onreplacestate = function(e) {
  91. window.dispatchEvent(new CustomEvent('replacestate', e));
  92. };
  93.  
  94. // Événements sur les en-têtes de tableau
  95. var theads = document.querySelectorAll('thead');
  96. theads.forEach(function(thead) {
  97. thead.addEventListener('click', function(event) {
  98. customFunction('Un thead a été cliqué' + event.target);
  99. });
  100. });
  101.  
  102. function transformUrlsToLinks() {
  103. const cells = document.querySelectorAll('.beWvMU');
  104. const urlRegex = /(https?:\/\/[^\s]+)/g;
  105.  
  106. if (cells.length > 0) {
  107. cells.forEach((cell, index) => {
  108. const textContent = cell.textContent;
  109.  
  110. if (urlRegex.test(textContent)) {
  111. const link = document.createElement('a');
  112. link.setAttribute('href', textContent.match(urlRegex)[0]);
  113. link.textContent = textContent.match(urlRegex)[0];
  114. link.classList.add('custom-button');
  115. cell.textContent = '';
  116. cell.appendChild(link);
  117. }
  118. });
  119. }
  120. }
  121.  
  122. function checkUrlAndRunScriptTransformUrlsToLinks() {
  123. if (window.location.pathname.startsWith('/agent/filters/')) {
  124. transformUrlsToLinks();
  125. }
  126. }
  127.  
  128. function initializeInputLinks() {
  129. var inputElements = document.querySelectorAll('.custom_field_14504424601628 input');
  130.  
  131. inputElements.forEach(function(inputElement) {
  132. var existingLink = inputElement.parentNode.parentNode.querySelector('.djm-task-link');
  133. if (existingLink) {
  134. existingLink.remove();
  135. }
  136.  
  137. var linkElement = document.createElement('a');
  138. linkElement.textContent = 'Tâche';
  139. linkElement.style.display = 'none';
  140. linkElement.classList.add('djm-task-link');
  141. inputElement.parentNode.parentNode.insertBefore(linkElement, inputElement.nextSibling);
  142.  
  143. checkForUrl(inputElement, linkElement);
  144.  
  145. inputElement.addEventListener('input', function() {
  146. console.log('Événement input détecté.');
  147. checkForUrl(inputElement, linkElement);
  148. });
  149. });
  150. }
  151.  
  152. function checkForUrl(inputElement, linkElement) {
  153. if (inputElement.value.match(/(https?:\/\/[^\s]+)/g)) {
  154. linkElement.href = inputElement.value;
  155. linkElement.style.display = 'inline-block';
  156. } else {
  157. linkElement.style.display = 'none';
  158. }
  159. }
  160.  
  161. function checkUrlAndRunScriptInitializeInputLinks() {
  162. if (window.location.pathname.startsWith('/agent/tickets/')) {
  163. initializeInputLinks();
  164. }
  165. }
  166.  
  167. // Fonction pour fermer tous les onglets inactifs
  168. function closeInactiveTabs() {
  169. const closeButtons = [...document.querySelectorAll('div[role="tab"][data-selected="false"] button[data-test-id="close-button"]')];
  170. closeButtons.forEach(btn => btn.click());
  171. }
  172.  
  173. // Fonction pour ajouter le bouton "Close All"
  174. function addCloseAllButton() {
  175. const toolbar = document.querySelector('div[data-test-id="header-tablist"] > div.sc-19uji9v-0');
  176. if (toolbar && !document.getElementById('close-all-button')) {
  177. const closeButton = document.createElement('button');
  178. closeButton.id = 'close-all-button';
  179. closeButton.textContent = 'X Close all';
  180. closeButton.className = 'custom-button';
  181. closeButton.addEventListener('click', closeInactiveTabs);
  182.  
  183. toolbar.appendChild(closeButton);
  184. }
  185. }
  186.  
  187. // Ajout du bouton "Close All" au chargement de la page
  188. window.addEventListener('load', addCloseAllButton);
  189.  
  190. // Observer les changements dans le DOM pour charger dynamiquement le bouton
  191. const observer = new MutationObserver(addCloseAllButton);
  192. observer.observe(document.body, { childList: true, subtree: true });
  193.  
  194.  
  195.  
  196. function addCopyButtons() {
  197. // Get all 'ember-view workspace has-play' elements
  198. const workspaces = document.querySelectorAll('.ember-view.workspace');
  199.  
  200. workspaces.forEach((workspace, index) => {
  201. // Find the 'sc-1nvv38f-3 cjpyOe' element within each workspace
  202. const headerElement = workspace.querySelector('.sc-1nvv38f-3.cjpyOe');
  203.  
  204. if (headerElement) {
  205. // Check if the button already exists to avoid duplicates
  206. if (!headerElement.querySelector('.copy-conversation-button')) {
  207. // Create a new button
  208. const button = document.createElement('button');
  209. button.innerText = 'Copy Conversation';
  210. button.style.marginLeft = '10px';
  211. button.classList.add('copy-conversation-button'); // Add a class for easy selection
  212.  
  213. // Add the click event to copy conversation text
  214. button.addEventListener('click', () => {
  215. // Find the conversation text container
  216. const conversationContainer = workspace.querySelector('.sc-175iuw8-0.ecaNtR.conversation-polaris.polaris-react-component.rich_text');
  217.  
  218. if (conversationContainer) {
  219. const textToCopy = conversationContainer.innerText;
  220.  
  221. // Copy the inner text of the conversation container to clipboard
  222. copyToClipboard(textToCopy);
  223. }
  224. });
  225.  
  226. // Append the button to the header element
  227. headerElement.appendChild(button);
  228. }
  229. }
  230. });
  231. }
  232.  
  233. // Function to copy text to clipboard
  234. function copyToClipboard(text) {
  235. navigator.clipboard.writeText(text).catch(err => {
  236. });
  237. }
  238.  
  239. // Run the function every second
  240. setInterval(addCopyButtons, 1000);
  241.  
  242.  
  243.  
  244.  
  245. })();