{"version":3,"file":"internal.js","mappings":";;;;;;;;;AAAA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AASA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;;;;;;;;;;;;;AC9FA;AACA;AACA;AAAA;AAEA;AAAA;AAAA;AAAA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAAA;AAEA;AAAA;AAAA;AAAA;AACA;AACA;AAAA;AACA;AAAA;AAAA;AAAA;AACA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AACA;AAPA;AAAA;AAAA;AAOA;AAAA;AAAA;AAAA;AAAA;AAAA;AAEA;AAAA;AAEA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAPA;AAAA;AAAA;AAOA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;;;;;;;;;;ACzDA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;;;;;;;;;;;;;;;AChBA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAMA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAQA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AAAA;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AAAA;AAAA;AACA;AAGA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AAGA;AACA;AACA;AAAA;AAAA;AAAA;AAGA;AACA;AACA;AAAA;AAAA;AAAA;AAGA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AAGA;AACA;AACA;AACA;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AAEA;AASA;AACA;AACA;AACA;AACA;AAQA;AACA;AASA;AACA;AACA;;AAEA;AACA;AAiBA;AACA;AACA;AACA;AAUA;AACA;AASA;AACA;AAgBA;AAEA;AACA;;AAEA;AACA;AACA;AAAA;AAAA;AAAA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AACA;AACA;AAAA;AAEA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AAEA;AACA;AACA;AAAA;AAAA;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AACA;AACA;AAAA;AAEA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AACA;AACA;AAAA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AAGA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AAGA;AACA;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AACA;AACA;AAAA;AAEA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AAGA;AACA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AAGA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AAIA;;;;;;;;;;AC5eA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AADA;AAAA;AAEA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAAA;AAAA;AAAA;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AAAA;AAAA;AAAA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAAA;AAAA;AACA;AAEA;AACA;AAAA;AAAA;AACA;AAEA;AACA;AAAA;AAAA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AAAA;AAEA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AAXA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAcA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AAAA;AAAA;;AAEA;AACA;AACA;AAAA;AAAA;AAAA;AAEA;AACA;AAEA;AAwBA;AAAA;AAMA;AAAA;AAAA;AAAA;AAGA;AAAA;AAAA;AACA;AAAA;AAAA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAGA;AAIA;AA0BA;AAAA;AAAA;AAAA;AAEA;AACA;AACA;AAEA;AAAA;AAAA;AACA;AAAA;AAAA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAAA;AAAA;AAEA;AACA;AACA;AACA;AAEA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAEA;AACA;AAIA;AAAA;AACA;AAGA;AA2BA;AAAA;AAAA;AAAA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AAAA;AAEA;AAAA;AAAA;AAAA;AACA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAIA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AACA;AAAA;AAPA;AAAA;AAAA;AASA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAEA;AACA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAEA;AA1BA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA6BA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AAGA;AACA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AAGA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AAAA;AAEA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAGA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AAAA;AACA;AAAA;AAPA;AAAA;AAAA;AASA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAEA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAEA;AAvBA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAyBA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAGA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AAAA;AACA;AAAA;AAPA;AAAA;AAAA;AASA;AAAA;AACA;AACA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAEA;AACA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAEA;AAzBA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA2BA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAGA;AACA;AACA;AACA;AACA;AACA;AAAA;AANA;AAAA;AAAA;AAQA;AAAA;AACA;AACA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAEA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAEA;AA7BA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA+BA;AAAA;AAAA;AAAA;AACA;AAEA;AAAA;AAAA;AAIA;AACA;AACA;AACA;AACA;AACA;AAAA;AANA;AAAA;AAAA;AAQA;AAAA;AACA;AACA;AACA;AAAA;AAAA;AACA;AACA;AACA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAEA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAEA;AAjCA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAmCA;AAAA;AAAA;AAAA;AACA;AAEA;AAAA;AAAA;AAIA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AACA;AAAA;AAPA;AAAA;AAAA;AASA;AAAA;AACA;AACA;AACA;AAAA;AAAA;AACA;AACA;AACA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAEA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAEA;AAtCA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAwCA;AAAA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAAA;AACA;AAFA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAKA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AAGA;AACA;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AAAA;AAAA;AAIA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;;;;;;;;ACtoBA;;;;;;;ACAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;AC7BA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;;;;;;ACNA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AAEA;AACA;AACA;AACA;AAEA;AAAA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AAEA;AAEA;;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAGA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AAEA;;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AAAA;;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AAAA;AAAA;AAAA;AACA;AACA;AACA;;AAEA;AACA","sources":["webpack://ctatus/./app/javascript/packs/notifications.js","webpack://ctatus/./app/javascript/packs/service_show.js","webpack://ctatus/./app/javascript/packs/services.js","webpack://ctatus/./app/javascript/profile_survey.js","webpack://ctatus/./app/javascript/sections_manager.js","webpack://ctatus/./app/javascript/stylesheets/internal.scss?f500","webpack://ctatus/webpack/bootstrap","webpack://ctatus/webpack/runtime/make namespace object","webpack://ctatus/./app/javascript/packs/internal.js"],"sourcesContent":["function isValidHttpUrl(string) {\n var res = string.match(/(http(s)?:\\/\\/.)?(www\\.)?[-a-zA-Z0-9@:%._\\+~#=]{2,256}\\.[a-z]{2,6}\\b([-a-zA-Z0-9@:%_\\+.~#?&//=]*)/g);\n return (res !== null)\n}\n\ndocument.addEventListener(\"DOMContentLoaded\", (e) => {\n let toggles = document.querySelectorAll(\".js-checkbox-status\");\n\n toggles.forEach(toggle => {\n toggle.addEventListener('click', function() {\n const checkbox = this.querySelector('input[type=\"checkbox\"]');\n const info = this.closest('.notification-modal').querySelector('.modal-form');\n const span = this.querySelector('span');\n\n checkbox.checked = !checkbox.checked;\n\n if(checkbox.checked) {\n this.classList.remove('bg-gray-400');\n this.classList.add('bg-blue-600');\n span.classList.remove('translate-x-0');\n span.classList.add('translate-x-5');\n info.classList.remove('hidden');\n } else {\n this.classList.remove('bg-blue-600');\n this.classList.add('bg-gray-400');\n span.classList.remove('translate-x-5');\n span.classList.add('translate-x-0');\n info.classList.add('hidden');\n }\n\n this.setAttribute('aria-checked', checkbox.checked);\n });\n });\n\n let webhookUrl = document.getElementById('webhook_url');\n\n if(webhookUrl){\n webhookUrl.addEventListener('keyup', function() {\n if(!isValidHttpUrl(this.value)){\n this.classList.remove('bg-green-100');\n this.classList.add('bg-red-200');\n } else {\n this.classList.remove('bg-red-200');\n this.classList.add('bg-green-100');\n }\n })\n }\n\n // FreshService custom fields functionality\n const handleCustomFields = () => {\n // Add custom field button handler\n document.querySelectorAll('.add-custom-field').forEach(button => {\n button.addEventListener('click', function(e) {\n e.preventDefault();\n const container = this.previousElementSibling;\n const newField = document.createElement('div');\n newField.className = 'flex gap-2 custom-field-row items-center';\n newField.innerHTML = `\n \n \n \n `;\n container.appendChild(newField);\n attachRemoveHandlers();\n });\n });\n\n // Function to attach remove handlers\n const attachRemoveHandlers = () => {\n document.querySelectorAll('.remove-custom-field').forEach(button => {\n button.removeEventListener('click', removeFieldHandler);\n button.addEventListener('click', removeFieldHandler);\n });\n };\n\n // Remove field handler\n const removeFieldHandler = function(e) {\n e.preventDefault();\n this.closest('.custom-field-row').remove();\n };\n\n // Initial attachment of remove handlers\n attachRemoveHandlers();\n };\n\n // Initialize custom fields handlers\n handleCustomFields();\n\n // Re-initialize when modals are opened (for dynamically loaded content)\n document.addEventListener('modal:opened', handleCustomFields);\n});","function prepareComponents(){\n let notificationsType = document.querySelector('input[name=\"connection[notify_by_component]\"]:checked').value;\n let components = document.getElementsByClassName('component');\n\n for(let component of components){\n\n let name = component.getElementsByClassName('name')[0];\n let check = component.getElementsByClassName('check')[0];\n\n if( notificationsType == 'every' ) {\n name.classList.remove('w-11/12')\n name.classList.add('w-full');\n check.classList.add('hidden');\n } else {\n name.classList.remove('w-full')\n name.classList.add('w-11/12');\n check.classList.remove('hidden');\n }\n }\n}\n\ndocument.addEventListener(\"DOMContentLoaded\", (e) => {\n let radios = document.getElementsByName('connection[notify_by_component]');\n\n if(radios.length == 0){\n return;\n }\n\n prepareComponents()\n\n radios.forEach(radio => {\n radio.addEventListener('change', function() {\n prepareComponents()\n });\n });\n\n let parent_components = document.getElementsByClassName('cparent');\n\n for(let parent of parent_components){\n parent.addEventListener('change', function() {\n children = document.querySelectorAll(\"[data-parent='\"+parent.value+\"']\")\n for(let child of children){\n child.checked = parent.checked;\n }\n });\n };\n\n let children_components = document.getElementsByClassName('cchild');\n\n for(let child of children_components){\n child.addEventListener('change', function() {\n parent = document.getElementById(\"connection_components_ids_\"+child.getAttribute('data-parent'))\n if(!child.checked){\n parent.checked = false\n }\n });\n };\n});","document.addEventListener(\"DOMContentLoaded\", (e) => {\n toggle_list = document.querySelectorAll('.js-toggle-list');\n\n toggle_list.forEach(tlist => {\n tlist.addEventListener('click', (e) => {\n list = e.srcElement.parentElement.nextElementSibling;\n\n if(list.classList.contains('hidden')){\n list.classList.remove('hidden');\n } else {\n list.classList.add('hidden');\n }\n\n e.preventDefault();\n });\n });\n});","class ProfileSurvey {\n constructor(config) {\n this.surveyPath = config.surveyPath;\n this.skipPath = config.skipPath;\n this.surveyQuestions = {\n primary_role: {\n text: \"Which best describes your role?\",\n type: \"single_choice\",\n options: [\"SRE / DevOps\", \"IT Manager\", \"Engineering Lead\", \"CTO\", \"Other\"]\n },\n other_roles: {\n text: \"Who else in your company will need outage alerts?\",\n type: \"multi_choice\",\n options: [\"Engineering\", \"IT Ops\", \"Customer Support\", \"Product\", \"Leadership\"]\n },\n primary_goal: {\n text: \"What's the main job you're hiring IsDown to do?\",\n type: \"single_choice\",\n options: [\n \"Know first when vendors go down\",\n \"Share vendor status on our status page\",\n \"Track vendor reliability over time\",\n \"Other\"\n ]\n },\n // main_objection: {\n // text: \"Biggest hesitation before paying for IsDown?\",\n // type: \"single_choice\",\n // options: [\n // \"Price\",\n // \"Not sure we'd use it enough\",\n // \"We might build it ourselves\",\n // \"It's not a priority right now\",\n // \"Other\"\n // ]\n // },\n how_did_you_find_us: {\n text: \"How did you find us?\",\n type: \"single_choice\",\n options: [\n \"Google / Bing Search\",\n \"Social Media\",\n \"Recommendation from colleague\",\n \"Blog\",\n \"ChatGPT / Claude / Other LLM\",\n \"Other\"\n ]\n },\n first_name_profile: {\n text: \"Tell us your first name so we can say hi properly\",\n type: \"text\",\n optional: true\n }\n };\n\n this.questionOrder = ['primary_role', 'other_roles', 'primary_goal', 'how_did_you_find_us', /* 'main_objection', */ 'first_name_profile'];\n this.currentQuestionIndex = 0;\n this.surveyAnswers = {};\n\n // Bind methods to this context\n this.initialize = this.initialize.bind(this);\n this.showModal = this.showModal.bind(this);\n this.hideModal = this.hideModal.bind(this);\n this.closeModal = this.closeModal.bind(this);\n this.displayCurrentQuestion = this.displayCurrentQuestion.bind(this);\n this.submitAnswer = this.submitAnswer.bind(this);\n this.submitMultiChoice = this.submitMultiChoice.bind(this);\n this.submitText = this.submitText.bind(this);\n this.skipQuestion = this.skipQuestion.bind(this);\n this.completeSurvey = this.completeSurvey.bind(this);\n this.showOtherInput = this.showOtherInput.bind(this);\n this.submitOtherAnswer = this.submitOtherAnswer.bind(this);\n this.showError = this.showError.bind(this);\n this.clearError = this.clearError.bind(this);\n }\n\n initialize() {\n // Check if user manually closed the modal before\n if (localStorage.getItem('profileSurveyClosed') === 'true') {\n return; // Don't show if user explicitly closed it\n }\n\n // Load saved progress from localStorage\n const savedProgress = localStorage.getItem('profileSurveyProgress');\n if (savedProgress) {\n this.surveyAnswers = JSON.parse(savedProgress);\n // Find the first unanswered question\n for (let i = 0; i < this.questionOrder.length; i++) {\n if (!this.surveyAnswers[this.questionOrder[i]] && !this.surveyAnswers[this.questionOrder[i] + '_skipped']) {\n this.currentQuestionIndex = i;\n break;\n }\n }\n }\n\n // Check if survey should be shown\n fetch(`${this.surveyPath}.json`)\n .then(response => response.json())\n .then(data => {\n // Check if survey is already completed\n if (data.survey_completed) {\n return;\n }\n\n // Load any existing answers\n if (data.answers) {\n Object.keys(data.answers).forEach(key => {\n if (!key.endsWith('_skipped') && data.answers[key]) {\n this.surveyAnswers[key] = data.answers[key];\n }\n });\n }\n\n // Check if we should show the modal\n const mandatoryQuestions = ['primary_role', 'other_roles', 'primary_goal', 'how_did_you_find_us' /*, 'main_objection' */];\n const mandatoryComplete = mandatoryQuestions.every(q =>\n this.surveyAnswers[q] || data.answers?.[`${q}_skipped`]\n );\n\n if (!mandatoryComplete) {\n this.showModal();\n this.displayCurrentQuestion();\n }\n });\n }\n\n showModal() {\n document.getElementById('profile-survey-modal').classList.remove('hidden');\n document.body.style.overflow = 'hidden'; // Prevent body scroll\n }\n\n hideModal() {\n document.getElementById('profile-survey-modal').classList.add('hidden');\n document.body.style.overflow = ''; // Restore body scroll\n }\n\n closeModal() {\n // Save current state to localStorage\n localStorage.setItem('profileSurveyClosed', 'true');\n this.hideModal();\n }\n\n displayCurrentQuestion() {\n if (this.currentQuestionIndex >= this.questionOrder.length) {\n this.completeSurvey();\n return;\n }\n\n const questionKey = this.questionOrder[this.currentQuestionIndex];\n const question = this.surveyQuestions[questionKey];\n const container = document.getElementById('survey-question-container');\n\n // Update progress bar and text\n const progress = ((this.currentQuestionIndex + 1) / this.questionOrder.length) * 100;\n document.getElementById('survey-progress-bar').style.width = progress + '%';\n document.getElementById('survey-progress-text').textContent = `Question ${this.currentQuestionIndex + 1} of ${this.questionOrder.length}`;\n\n let html = `\n
\n
\n

\n ${question.text}\n ${question.optional ? '(optional)' : ''}\n

\n `;\n\n if (question.type === 'single_choice') {\n html += '
';\n question.options.forEach(option => {\n if (option === \"Other\") {\n html += `\n \n ${option}\n \n `;\n } else {\n html += `\n \n ${option}\n \n `;\n }\n });\n html += '
';\n\n // Add hidden input field for \"Other\" option\n html += `\n
\n

Please specify:

\n \n \n Continue →\n \n
\n `;\n } else if (question.type === 'multi_choice') {\n html += '
';\n question.options.forEach(option => {\n html += `\n \n `;\n });\n html += `\n
\n \n Continue →\n \n `;\n } else if (question.type === 'text') {\n html += `\n
\n \n \n ${question.optional ? 'Continue →' : 'Submit →'}\n \n
\n `;\n }\n\n html += '
';\n container.innerHTML = html;\n\n // Add event listeners to the new elements\n this.attachEventListeners();\n }\n\n attachEventListeners() {\n // Single choice buttons\n document.querySelectorAll('[data-action=\"submit-answer\"]').forEach(button => {\n button.addEventListener('click', (e) => {\n const question = e.target.dataset.question;\n const answer = e.target.dataset.answer;\n this.submitAnswer(question, answer);\n });\n });\n\n // Show other input field buttons\n document.querySelectorAll('[data-action=\"show-other-input\"]').forEach(button => {\n button.addEventListener('click', (e) => {\n const question = e.target.dataset.question;\n this.showOtherInput(question);\n });\n });\n\n // Submit other input buttons\n document.querySelectorAll('[data-action=\"submit-other\"]').forEach(button => {\n button.addEventListener('click', (e) => {\n const question = e.target.dataset.question;\n this.submitOtherAnswer(question);\n });\n });\n\n // Other input enter key\n document.querySelectorAll('[id^=\"other-input-\"]').forEach(input => {\n input.addEventListener('keypress', (e) => {\n if (e.key === 'Enter') {\n const question = e.target.dataset.question;\n this.submitOtherAnswer(question);\n }\n });\n });\n\n // Multi choice submit button\n const multiSubmit = document.querySelector('[data-action=\"submit-multi\"]');\n if (multiSubmit) {\n multiSubmit.addEventListener('click', (e) => {\n const question = e.target.dataset.question;\n this.submitMultiChoice(question);\n });\n }\n\n // Text input submit button\n const textSubmit = document.querySelector('[data-action=\"submit-text\"]');\n if (textSubmit) {\n textSubmit.addEventListener('click', (e) => {\n const question = e.target.dataset.question;\n this.submitText(question);\n });\n }\n\n // Text input enter key\n const textInput = document.getElementById('survey-text-input');\n if (textInput) {\n textInput.addEventListener('keypress', (e) => {\n if (e.key === 'Enter') {\n const question = e.target.dataset.question;\n this.submitText(question);\n }\n });\n }\n }\n\n submitAnswer(question, answer) {\n this.surveyAnswers[question] = answer;\n localStorage.setItem('profileSurveyProgress', JSON.stringify(this.surveyAnswers));\n\n fetch(this.surveyPath, {\n method: 'PATCH',\n headers: {\n 'Content-Type': 'application/json',\n 'X-CSRF-Token': document.querySelector('[name=\"csrf-token\"]').content\n },\n body: JSON.stringify({ question: question, answer: answer })\n })\n .then(response => response.json())\n .then(() => {\n this.currentQuestionIndex++;\n this.displayCurrentQuestion();\n });\n }\n\n submitMultiChoice(question) {\n const checkboxes = document.querySelectorAll('input[name=\"survey_multi_choice\"]:checked');\n const answers = Array.from(checkboxes).map(cb => cb.value);\n\n if (answers.length === 0) {\n this.showError('Please select at least one option');\n return;\n }\n\n this.surveyAnswers[question] = answers;\n localStorage.setItem('profileSurveyProgress', JSON.stringify(this.surveyAnswers));\n\n fetch(this.surveyPath, {\n method: 'PATCH',\n headers: {\n 'Content-Type': 'application/json',\n 'X-CSRF-Token': document.querySelector('[name=\"csrf-token\"]').content\n },\n body: JSON.stringify({ question: question, answer: answers })\n })\n .then(response => response.json())\n .then(() => {\n this.currentQuestionIndex++;\n this.displayCurrentQuestion();\n });\n }\n\n submitText(question) {\n const input = document.getElementById('survey-text-input');\n const answer = input.value.trim();\n\n if (!answer && !this.surveyQuestions[question].optional) {\n return;\n }\n\n if (answer) {\n this.surveyAnswers[question] = answer;\n localStorage.setItem('profileSurveyProgress', JSON.stringify(this.surveyAnswers));\n\n fetch(this.surveyPath, {\n method: 'PATCH',\n headers: {\n 'Content-Type': 'application/json',\n 'X-CSRF-Token': document.querySelector('[name=\"csrf-token\"]').content\n },\n body: JSON.stringify({ question: question, answer: answer })\n })\n .then(response => response.json())\n .then(() => {\n this.currentQuestionIndex++;\n this.displayCurrentQuestion();\n });\n } else {\n this.skipQuestion();\n }\n }\n\n showOtherInput(question) {\n // Hide all option buttons\n const container = document.getElementById('survey-question-container');\n const buttonsDiv = container.querySelector('.space-y-2');\n if (buttonsDiv) {\n buttonsDiv.style.display = 'none';\n }\n\n // Show the other input field\n const otherContainer = document.getElementById(`other-input-container-${question}`);\n if (otherContainer) {\n otherContainer.classList.remove('hidden');\n // Focus on the input field\n const input = document.getElementById(`other-input-${question}`);\n if (input) {\n input.focus();\n }\n }\n }\n\n submitOtherAnswer(question) {\n const input = document.getElementById(`other-input-${question}`);\n const answer = input ? input.value.trim() : '';\n\n if (!answer) {\n this.showError('Please provide your answer');\n return;\n }\n\n // Store as \"Other: [user input]\"\n const formattedAnswer = `Other: ${answer}`;\n this.submitAnswer(question, formattedAnswer);\n }\n\n skipQuestion() {\n const question = this.questionOrder[this.currentQuestionIndex];\n\n fetch(this.skipPath, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'X-CSRF-Token': document.querySelector('[name=\"csrf-token\"]').content\n },\n body: JSON.stringify({ question: question })\n })\n .then(response => response.json())\n .then(() => {\n this.surveyAnswers[question + '_skipped'] = true;\n localStorage.setItem('profileSurveyProgress', JSON.stringify(this.surveyAnswers));\n this.currentQuestionIndex++;\n this.displayCurrentQuestion();\n });\n }\n\n completeSurvey() {\n localStorage.removeItem('profileSurveyProgress');\n localStorage.removeItem('profileSurveyClosed');\n this.hideModal();\n // Optionally refresh the page or show a success message\n }\n\n showError(message) {\n const errorElement = document.getElementById('survey-error-message');\n if (errorElement) {\n errorElement.textContent = message;\n errorElement.classList.remove('hidden');\n\n // Add a subtle shake animation\n errorElement.style.animation = 'shake 0.3s';\n setTimeout(() => {\n if (errorElement) {\n errorElement.style.animation = '';\n }\n }, 300);\n\n // Auto-hide after 5 seconds\n setTimeout(() => {\n this.clearError();\n }, 5000);\n }\n }\n\n clearError() {\n const errorElement = document.getElementById('survey-error-message');\n if (errorElement) {\n errorElement.classList.add('hidden');\n errorElement.textContent = '';\n }\n }\n}\n\n// Export for use in other files\nwindow.ProfileSurvey = ProfileSurvey;","// Sections Manager - Handles sections and monitor visibility management\nclass SectionsManager {\n constructor() {\n this.profileUrlname = null;\n this.csrfToken = null;\n this.sections = [];\n this.connections = [];\n this.probes = [];\n this.draggedElement = null;\n this.draggedSectionId = null;\n\n this.init();\n }\n\n init() {\n // Get CSRF token\n this.csrfToken = document.querySelector('meta[name=\"csrf-token\"]')?.content;\n\n // Get profile urlname from URL\n const pathParts = window.location.pathname.split('/');\n const profileIndex = pathParts.indexOf('profiles');\n if (profileIndex !== -1 && pathParts[profileIndex + 1]) {\n this.profileUrlname = pathParts[profileIndex + 1];\n }\n\n // Set up event listeners\n this.setupEventListeners();\n\n // Load initial data\n this.loadData();\n }\n\n setupEventListeners() {\n const createBtn = document.getElementById('create-section-btn');\n const modal = document.getElementById('create-section-modal');\n const cancelBtn = document.getElementById('cancel-section-btn');\n const saveBtn = document.getElementById('save-section-btn');\n const nameInput = document.getElementById('section-name-input');\n\n if (createBtn) {\n createBtn.addEventListener('click', () => this.showCreateSectionModal());\n }\n\n if (cancelBtn) {\n cancelBtn.addEventListener('click', () => this.hideCreateSectionModal());\n }\n\n if (saveBtn) {\n saveBtn.addEventListener('click', () => this.handleCreateSection());\n }\n\n if (nameInput) {\n nameInput.addEventListener('keypress', (e) => {\n if (e.key === 'Enter') {\n this.handleCreateSection();\n }\n });\n }\n\n // Close modal when clicking outside\n if (modal) {\n modal.addEventListener('click', (e) => {\n if (e.target === modal) {\n this.hideCreateSectionModal();\n }\n });\n }\n }\n\n async loadData() {\n // Load data from server-rendered elements\n const sectionsData = window.sectionsData || [];\n const connectionsData = window.connectionsData || [];\n const probesData = window.probesData || [];\n\n this.sections = sectionsData;\n this.connections = connectionsData;\n this.probes = probesData;\n\n this.render();\n }\n\n render() {\n this.renderSections();\n this.renderUnsectionedMonitors();\n this.setupMonitorEventListeners();\n }\n\n renderSections() {\n const container = document.getElementById('sections-container');\n if (!container) return;\n\n if (this.sections.length === 0) {\n container.innerHTML = '

No sections created yet. Create your first section to get started.

';\n return;\n }\n\n container.innerHTML = this.sections.map(section => this.createSectionHTML(section)).join('');\n\n // Setup drag and drop for sections\n this.setupSectionDragAndDrop();\n }\n\n createSectionHTML(section) {\n const sectionMonitors = this.getMonitorsForSection(section.id);\n\n return `\n
\n
\n
\n \n \n \n \n
\n \n
\n
\n ${sectionMonitors.length > 0 ?\n sectionMonitors.map(monitor => this.createMonitorHTML(monitor)).join('') :\n '

No monitors in this section

'\n }\n
\n
\n `;\n }\n\n getMonitorsForSection(sectionId) {\n const sectionConnections = this.connections.filter(c => c.section_id === sectionId);\n const sectionProbes = this.probes.filter(p => p.section_id === sectionId);\n\n const monitors = [];\n sectionConnections.forEach(conn => {\n monitors.push({\n id: conn.id,\n type: 'connection',\n name: conn.name || conn.service_name,\n visible: conn.visible_on_status_page,\n section_id: conn.section_id\n });\n });\n\n sectionProbes.forEach(probe => {\n monitors.push({\n id: probe.id,\n type: 'probe',\n name: probe.name,\n visible: probe.visible_on_status_page,\n section_id: probe.section_id\n });\n });\n\n return monitors.sort((a, b) => a.name.localeCompare(b.name));\n }\n\n createMonitorHTML(monitor) {\n const eyeIcon = monitor.visible ?\n '' :\n '';\n\n return `\n
\n
\n \n ${this.escapeHtml(monitor.name)}\n
\n \n
\n `;\n }\n\n renderUnsectionedMonitors() {\n const container = document.getElementById('unsectioned-monitors-container');\n if (!container) return;\n\n const unsectionedConnections = this.connections.filter(c => !c.section_id);\n const unsectionedProbes = this.probes.filter(p => !p.section_id);\n\n const monitors = [];\n unsectionedConnections.forEach(conn => {\n monitors.push({\n id: conn.id,\n type: 'connection',\n name: conn.name || conn.service_name,\n visible: conn.visible_on_status_page\n });\n });\n\n unsectionedProbes.forEach(probe => {\n monitors.push({\n id: probe.id,\n type: 'probe',\n name: probe.name,\n visible: probe.visible_on_status_page\n });\n });\n\n monitors.sort((a, b) => a.name.localeCompare(b.name));\n\n if (monitors.length === 0) {\n container.innerHTML = '

All monitors are assigned to sections.

';\n return;\n }\n\n container.innerHTML = monitors.map(monitor => this.createUnsectionedMonitorHTML(monitor)).join('');\n }\n\n createUnsectionedMonitorHTML(monitor) {\n const eyeIcon = monitor.visible ?\n '' :\n '';\n\n const sectionsOptions = this.sections.map(s =>\n ``\n ).join('');\n\n return `\n
\n
\n \n ${this.escapeHtml(monitor.name)}\n
\n
\n ${this.sections.length > 0 ? `\n \n ` : ''}\n
\n
\n `;\n }\n\n setupMonitorEventListeners() {\n // Visibility toggles\n document.querySelectorAll('.toggle-visibility-btn').forEach(btn => {\n btn.addEventListener('click', (e) => {\n const monitorId = e.currentTarget.dataset.monitorId;\n const monitorType = e.currentTarget.dataset.monitorType;\n this.toggleVisibility(monitorId, monitorType);\n });\n });\n\n // Add to section dropdowns\n document.querySelectorAll('.add-to-section-select').forEach(select => {\n select.addEventListener('change', (e) => {\n const monitorId = e.currentTarget.dataset.monitorId;\n const monitorType = e.currentTarget.dataset.monitorType;\n const sectionId = e.currentTarget.value;\n if (sectionId) {\n this.assignToSection(monitorId, monitorType, sectionId);\n }\n });\n });\n\n // Remove from section buttons\n document.querySelectorAll('.remove-from-section-btn').forEach(btn => {\n btn.addEventListener('click', (e) => {\n const monitorId = e.currentTarget.dataset.monitorId;\n const monitorType = e.currentTarget.dataset.monitorType;\n this.removeFromSection(monitorId, monitorType);\n });\n });\n\n // Section name inputs\n document.querySelectorAll('.section-name-input').forEach(input => {\n let lastSavedValue = input.value;\n\n // Save on blur if value has changed\n input.addEventListener('blur', (e) => {\n const newName = e.currentTarget.value;\n if (newName !== lastSavedValue) {\n const sectionId = e.currentTarget.dataset.sectionId;\n this.updateSectionName(sectionId, newName);\n lastSavedValue = newName;\n }\n });\n\n // Prevent form submission on Enter key and trigger immediate save\n input.addEventListener('keypress', (e) => {\n if (e.key === 'Enter') {\n e.preventDefault();\n const sectionId = e.currentTarget.dataset.sectionId;\n const newName = e.currentTarget.value;\n this.updateSectionName(sectionId, newName);\n lastSavedValue = newName;\n e.currentTarget.blur();\n }\n });\n });\n\n // Delete section buttons\n document.querySelectorAll('.delete-section-btn').forEach(btn => {\n btn.addEventListener('click', (e) => {\n const sectionId = e.currentTarget.dataset.sectionId;\n if (confirm('Are you sure you want to delete this section? Monitors will be moved to \"Unsectioned\".')) {\n this.deleteSection(sectionId);\n }\n });\n });\n }\n\n setupSectionDragAndDrop() {\n const sectionItems = document.querySelectorAll('.section-item');\n\n sectionItems.forEach(item => {\n item.addEventListener('dragstart', (e) => {\n this.draggedElement = e.currentTarget;\n this.draggedSectionId = e.currentTarget.dataset.sectionId;\n e.currentTarget.style.opacity = '0.5';\n e.dataTransfer.effectAllowed = 'move';\n });\n\n item.addEventListener('dragend', (e) => {\n e.currentTarget.style.opacity = '1';\n this.draggedElement = null;\n this.draggedSectionId = null;\n });\n\n item.addEventListener('dragover', (e) => {\n e.preventDefault();\n e.dataTransfer.dropEffect = 'move';\n\n if (this.draggedElement && e.currentTarget !== this.draggedElement) {\n const rect = e.currentTarget.getBoundingClientRect();\n const midpoint = rect.top + rect.height / 2;\n\n if (e.clientY < midpoint) {\n e.currentTarget.parentNode.insertBefore(this.draggedElement, e.currentTarget);\n } else {\n e.currentTarget.parentNode.insertBefore(this.draggedElement, e.currentTarget.nextSibling);\n }\n }\n });\n\n item.addEventListener('drop', (e) => {\n e.preventDefault();\n this.saveSectionOrder();\n });\n });\n }\n\n async saveSectionOrder() {\n const sectionItems = document.querySelectorAll('.section-item');\n const sectionsOrder = Array.from(sectionItems).map(item => item.dataset.sectionId);\n\n try {\n const response = await fetch(`/profiles/${this.profileUrlname}/sections/reorder`, {\n method: 'PATCH',\n headers: {\n 'Content-Type': 'application/json',\n 'X-CSRF-Token': this.csrfToken\n },\n body: JSON.stringify({ sections_order: sectionsOrder })\n });\n\n const data = await response.json();\n if (data.success) {\n this.showSuccessMessage('Sections reordered successfully');\n } else {\n alert('Failed to reorder sections');\n this.render();\n }\n } catch (error) {\n console.error('Error reordering sections:', error);\n alert('Failed to reorder sections');\n this.render();\n }\n }\n\n showCreateSectionModal() {\n const modal = document.getElementById('create-section-modal');\n const nameInput = document.getElementById('section-name-input');\n if (modal && nameInput) {\n nameInput.value = '';\n modal.classList.remove('hidden');\n nameInput.focus();\n }\n }\n\n hideCreateSectionModal() {\n const modal = document.getElementById('create-section-modal');\n if (modal) {\n modal.classList.add('hidden');\n }\n }\n\n handleCreateSection() {\n const nameInput = document.getElementById('section-name-input');\n const name = nameInput?.value.trim();\n\n if (name) {\n this.createSection(name);\n this.hideCreateSectionModal();\n } else {\n alert('Please enter a section name');\n nameInput?.focus();\n }\n }\n\n async createSection(name) {\n try {\n const response = await fetch(`/profiles/${this.profileUrlname}/sections`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'X-CSRF-Token': this.csrfToken\n },\n body: JSON.stringify({ section: { name } })\n });\n\n const data = await response.json();\n if (data.success) {\n this.sections.push(data.section);\n this.render();\n this.showSuccessMessage('Section created successfully');\n } else {\n alert('Failed to create section: ' + (data.errors || []).join(', '));\n }\n } catch (error) {\n console.error('Error creating section:', error);\n alert('Failed to create section');\n }\n }\n\n async updateSectionName(sectionId, name) {\n try {\n const response = await fetch(`/profiles/${this.profileUrlname}/sections/${sectionId}`, {\n method: 'PATCH',\n headers: {\n 'Content-Type': 'application/json',\n 'X-CSRF-Token': this.csrfToken\n },\n body: JSON.stringify({ section: { name } })\n });\n\n const data = await response.json();\n if (data.success) {\n const section = this.sections.find(s => s.id == sectionId);\n if (section) section.name = name;\n this.showSuccessMessage('Section name updated successfully');\n } else {\n alert('Failed to update section name: ' + (data.errors || data.error || 'Unknown error'));\n this.render();\n }\n } catch (error) {\n console.error('Error updating section:', error);\n alert('Failed to update section: ' + error.message);\n this.render();\n }\n }\n\n async deleteSection(sectionId) {\n try {\n const response = await fetch(`/profiles/${this.profileUrlname}/sections/${sectionId}`, {\n method: 'DELETE',\n headers: {\n 'Content-Type': 'application/json',\n 'X-CSRF-Token': this.csrfToken\n }\n });\n\n const data = await response.json();\n if (data.success) {\n this.sections = this.sections.filter(s => s.id != sectionId);\n // Update monitors that were in this section\n this.connections.forEach(c => {\n if (c.section_id == sectionId) c.section_id = null;\n });\n this.probes.forEach(p => {\n if (p.section_id == sectionId) p.section_id = null;\n });\n this.render();\n this.showSuccessMessage('Section deleted successfully');\n } else {\n alert('Failed to delete section');\n }\n } catch (error) {\n console.error('Error deleting section:', error);\n alert('Failed to delete section');\n }\n }\n\n async toggleVisibility(monitorId, monitorType) {\n const url = monitorType === 'connection' ?\n `/${this.profileUrlname}/connections/${monitorId}/toggle_visibility` :\n `/${this.profileUrlname}/monitors/${monitorId}/toggle_visibility`;\n\n try {\n const response = await fetch(url, {\n method: 'PATCH',\n headers: {\n 'Content-Type': 'application/json',\n 'X-CSRF-Token': this.csrfToken\n }\n });\n\n const data = await response.json();\n if (data.success) {\n if (monitorType === 'connection') {\n const conn = this.connections.find(c => c.id == monitorId);\n if (conn) conn.visible_on_status_page = data.visible;\n } else {\n const probe = this.probes.find(p => p.id == monitorId);\n if (probe) probe.visible_on_status_page = data.visible;\n }\n this.render();\n const visibilityState = data.visible ? 'visible' : 'hidden';\n this.showSuccessMessage(`Monitor ${visibilityState} on status page`);\n } else {\n alert('Failed to toggle visibility');\n }\n } catch (error) {\n console.error('Error toggling visibility:', error);\n alert('Failed to toggle visibility');\n }\n }\n\n async assignToSection(monitorId, monitorType, sectionId) {\n const url = monitorType === 'connection' ?\n `/${this.profileUrlname}/connections/${monitorId}/assign_section` :\n `/${this.profileUrlname}/monitors/${monitorId}/assign_section`;\n\n try {\n const response = await fetch(url, {\n method: 'PATCH',\n headers: {\n 'Content-Type': 'application/json',\n 'X-CSRF-Token': this.csrfToken\n },\n body: JSON.stringify({ section_id: sectionId })\n });\n\n const data = await response.json();\n if (data.success) {\n if (monitorType === 'connection') {\n const conn = this.connections.find(c => c.id == monitorId);\n if (conn) conn.section_id = parseInt(sectionId);\n } else {\n const probe = this.probes.find(p => p.id == monitorId);\n if (probe) probe.section_id = parseInt(sectionId);\n }\n this.render();\n if (sectionId) {\n const section = this.sections.find(s => s.id == sectionId);\n this.showSuccessMessage(`Monitor assigned to ${section ? section.name : 'section'}`);\n } else {\n this.showSuccessMessage('Monitor removed from section');\n }\n } else {\n alert('Failed to assign to section');\n }\n } catch (error) {\n console.error('Error assigning to section:', error);\n alert('Failed to assign to section');\n }\n }\n\n async removeFromSection(monitorId, monitorType) {\n await this.assignToSection(monitorId, monitorType, '');\n }\n\n escapeHtml(text) {\n const div = document.createElement('div');\n div.textContent = text;\n return div.innerHTML;\n }\n\n showSuccessMessage(message) {\n // Create or get alert container\n let alertContainer = document.querySelector('.js-alert-message');\n\n if (!alertContainer) {\n alertContainer = document.createElement('div');\n alertContainer.className = 'rounded-md my-2 fixed top-0 right-8 js-alert-message z-50';\n document.body.appendChild(alertContainer);\n }\n\n // Clear any existing content\n alertContainer.innerHTML = '';\n alertContainer.classList.remove('hidden');\n\n // Create success alert\n const alertElement = document.createElement('p');\n alertElement.className = 'alert alert-success rounded-md';\n alertElement.textContent = message;\n alertContainer.appendChild(alertElement);\n\n // Auto-hide after 3 seconds\n setTimeout(() => {\n alertContainer.classList.add('hidden');\n }, 3000);\n }\n}\n\n// Initialize when DOM is ready\nif (document.readyState === 'loading') {\n document.addEventListener('DOMContentLoaded', () => {\n if (document.getElementById('sections-container')) {\n window.sectionsManager = new SectionsManager();\n }\n });\n} else {\n if (document.getElementById('sections-container')) {\n window.sectionsManager = new SectionsManager();\n }\n}\n","// extracted by mini-css-extract-plugin\nexport {};","// The module cache\nvar __webpack_module_cache__ = {};\n\n// The require function\nfunction __webpack_require__(moduleId) {\n\t// Check if module is in cache\n\tvar cachedModule = __webpack_module_cache__[moduleId];\n\tif (cachedModule !== undefined) {\n\t\treturn cachedModule.exports;\n\t}\n\t// Check if module exists (development only)\n\tif (__webpack_modules__[moduleId] === undefined) {\n\t\tvar e = new Error(\"Cannot find module '\" + moduleId + \"'\");\n\t\te.code = 'MODULE_NOT_FOUND';\n\t\tthrow e;\n\t}\n\t// Create a new module (and put it into the cache)\n\tvar module = __webpack_module_cache__[moduleId] = {\n\t\t// no module.id needed\n\t\t// no module.loaded needed\n\t\texports: {}\n\t};\n\n\t// Execute the module function\n\t__webpack_modules__[moduleId](module, module.exports, __webpack_require__);\n\n\t// Return the exports of the module\n\treturn module.exports;\n}\n\n","// define __esModule on exports\n__webpack_require__.r = function(exports) {\n\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n\t}\n\tObject.defineProperty(exports, '__esModule', { value: true });\n};","// This file is automatically compiled by Webpack, along with any other files\n// present in this directory. You're encouraged to place your actual application logic in\n// a relevant structure within app/javascript and only use these pack files to reference\n// that code so it'll be compiled.\n\nrequire('stylesheets/internal.scss')\nrequire('./notifications')\nrequire('./services')\nrequire('./service_show')\nrequire('../profile_survey')\nrequire('../sections_manager')\n\ndocument.addEventListener(\"DOMContentLoaded\", (e) => {\n let close_popular = document.getElementById('js-close-popular');\n\n if(close_popular){\n close_popular.addEventListener('click', (e) => {\n document.cookie = 'close_popular=true; expires=Sun, 1 Jan 2030 00:00:00 UTC; path=/';\n document.getElementById('popular-services').remove();\n });\n }\n});\n\n// Mobile menu is no longer used since sidebar is always visible\n// Keeping event listeners in case they're needed later\n\ndocument.addEventListener(\"DOMContentLoaded\", (e) => {\n toggles = document.querySelectorAll('.js-toggle-button');\n\n toggles.forEach((toggle) => {\n toggle.addEventListener('click', (e) => {\n let target = e.target;\n let target_span = target.children[0];\n\n if(target.classList.contains('bg-gray-400')){ //disabled -> enabled\n target.classList.remove('bg-gray-400');\n target.classList.add('bg-blue-600');\n target_span.classList.remove('translate-x-0');\n target_span.classList.add('translate-x-5');\n\n setCookie('show_only_impacting', 'true', 365);\n toggleOnlyImpacting(true);\n } else { //enabled -> disabled\n target.classList.remove('bg-blue-600');\n target.classList.add('bg-gray-400');\n target_span.classList.remove('translate-x-5');\n target_span.classList.add('translate-x-0');\n\n setCookie('show_only_impacting', 'true', -1);\n toggleOnlyImpacting(false);\n\n }\n })\n });\n});\n\nfunction toggleOnlyImpacting(result){\n let outages = [].slice.call(document.getElementById('ongoing-outages').children);\n outages.forEach((child) => {\n if(result){\n if(!child.classList.contains('js-impact')){\n child.classList.add('hidden');\n }\n } else {\n child.classList.remove('hidden');\n }\n });\n}\n\nfunction getCookie(cname) {\n let name = cname + \"=\";\n let decodedCookie = decodeURIComponent(document.cookie);\n let ca = decodedCookie.split(';');\n for(let i = 0; i {\n option.addEventListener('click', function() {\n window.location = this.dataset.value;\n updateProfileTooltip();\n });\n });\n }\n },\n {\n triggerId: 'monitors-select',\n dropdownId: 'monitors-options',\n width: '260px',\n positionType: 'top'\n },\n {\n triggerId: 'setup-guide-select',\n dropdownId: 'setup-guide-options',\n width: '260px',\n positionType: 'bottom'\n },\n {\n triggerId: 'account-select',\n dropdownId: 'account-options',\n width: '256px',\n positionType: 'bottom' // positions from bottom\n }\n ];\n\n // Helper function to close all dropdowns except the specified one\n function closeOtherDropdowns(exceptId) {\n dropdowns.forEach(config => {\n if (config.dropdownId !== exceptId) {\n const dropdown = document.getElementById(config.dropdownId);\n if (dropdown) dropdown.classList.add('hidden');\n }\n });\n }\n\n // Initialize each dropdown\n dropdowns.forEach(config => {\n const trigger = document.getElementById(config.triggerId);\n const dropdown = document.getElementById(config.dropdownId);\n \n if (!trigger || !dropdown) return;\n \n let hoverTimeout;\n \n // Show dropdown on hover\n trigger.addEventListener('mouseenter', function() {\n clearTimeout(hoverTimeout);\n closeOtherDropdowns(config.dropdownId);\n \n const rect = trigger.getBoundingClientRect();\n const sidebar = document.getElementById('js-sidebar-menu');\n const sidebarRight = sidebar ? sidebar.getBoundingClientRect().right : rect.right;\n\n // Position dropdown flush with sidebar edge\n dropdown.style.left = sidebarRight + 'px';\n dropdown.style.width = config.width;\n \n if (config.positionType === 'bottom') {\n dropdown.style.bottom = (window.innerHeight - rect.bottom) + 'px';\n dropdown.style.top = 'auto';\n } else {\n dropdown.style.top = rect.top + 'px';\n dropdown.style.bottom = 'auto';\n \n // Set max height for top-positioned dropdowns\n if (config.triggerId === 'profile-select') {\n const viewportHeight = window.innerHeight;\n const spaceBelow = viewportHeight - rect.top - 20;\n dropdown.style.maxHeight = Math.min(400, spaceBelow) + 'px';\n dropdown.style.overflowY = 'auto';\n }\n }\n \n dropdown.classList.remove('hidden');\n });\n \n // Keep dropdown open when hovering over it\n dropdown.addEventListener('mouseenter', function() {\n clearTimeout(hoverTimeout);\n });\n \n // Hide on mouse leave with delay\n const hideDropdown = function() {\n hoverTimeout = setTimeout(function() {\n dropdown.classList.add('hidden');\n }, 300);\n };\n \n trigger.addEventListener('mouseleave', hideDropdown);\n dropdown.addEventListener('mouseleave', hideDropdown);\n \n // Apply custom handlers if specified\n if (config.customHandlers) {\n config.customHandlers(dropdown);\n }\n });\n \n // Global click handler to close dropdowns when clicking outside\n document.addEventListener('click', function(event) {\n dropdowns.forEach(config => {\n const trigger = document.getElementById(config.triggerId);\n const dropdown = document.getElementById(config.dropdownId);\n if (trigger && dropdown && !trigger.contains(event.target) && !dropdown.contains(event.target)) {\n dropdown.classList.add('hidden');\n }\n });\n });\n\n \n // Initialize other components\n initializeNoticeDismiss();\n initializeSidebarToggle();\n});\n\n// Notice dismiss functionality\nfunction initializeNoticeDismiss() {\n const dismissLink = document.getElementById('dismiss-feedback');\n if (!dismissLink) return;\n \n dismissLink.addEventListener('click', function(e) {\n e.preventDefault();\n e.stopPropagation();\n e.stopImmediatePropagation();\n\n fetch(dismissLink.dataset.href, {\n method: 'DELETE',\n headers: {\n 'X-CSRF-Token': document.querySelector('[name=\"csrf-token\"]').content\n },\n credentials: 'same-origin'\n }).then(() => {\n const notices = document.getElementById('notices');\n if (notices) notices.remove();\n });\n });\n}\n\n// Sidebar collapse functionality\nfunction initializeSidebarToggle() {\n const toggleBtns = ['js-toggle-sidebar', 'js-toggle-sidebar-collapsed'];\n const sidebar = document.getElementById('js-sidebar-menu');\n const mainContent = document.getElementById('main-content');\n \n if (!sidebar || !mainContent) return;\n \n // Add transitions after page load to prevent initial animation\n setTimeout(function() {\n sidebar.classList.add('transition-all', 'duration-300');\n mainContent.classList.add('transition-all', 'duration-300');\n }, 100);\n \n function toggleSidebar() {\n // Only allow toggle on desktop\n if (window.innerWidth < 1024) return;\n \n const isCollapsed = sidebar.classList.contains('sidebar-collapsed');\n const newState = !isCollapsed;\n \n // Toggle classes\n sidebar.classList.toggle('sidebar-collapsed', newState);\n mainContent.classList.toggle('lg:pl-16', newState);\n mainContent.classList.toggle('lg:pl-64', !newState);\n \n // Persist state\n const stateValue = newState ? 'true' : 'false';\n localStorage.setItem('sidebarCollapsed', stateValue);\n document.cookie = `sidebarCollapsed=${stateValue}; path=/; max-age=31536000`;\n }\n \n // Attach event listeners to both toggle buttons\n toggleBtns.forEach(btnId => {\n const btn = document.getElementById(btnId);\n if (btn) {\n btn.addEventListener('click', toggleSidebar);\n }\n });\n}\n\n// Helper function to update profile tooltip (no longer used but kept for compatibility)\nfunction updateProfileTooltip() {\n // Tooltips have been removed from the UI\n}\n\n// Component tree toggle functionality for status page monitors\nfunction initializeComponentTree() {\n // Handle component tree toggle\n const toggleButtons = document.querySelectorAll('.component-toggle-btn');\n toggleButtons.forEach(button => {\n button.addEventListener('click', function() {\n const targetId = this.dataset.target;\n const targetElement = document.getElementById(targetId);\n const chevron = this.querySelector('.chevron-icon');\n const isExpanded = this.getAttribute('aria-expanded') === 'true';\n\n if (targetElement) {\n if (isExpanded) {\n targetElement.classList.add('hidden');\n chevron.style.transform = 'rotate(0deg)';\n this.setAttribute('aria-expanded', 'false');\n } else {\n targetElement.classList.remove('hidden');\n chevron.style.transform = 'rotate(90deg)';\n this.setAttribute('aria-expanded', 'true');\n }\n }\n });\n });\n\n // Handle show all/show less\n const showAllBtn = document.getElementById('show-all-components');\n const showLessBtn = document.getElementById('show-less-components');\n const hiddenComponents = document.getElementById('hidden-components');\n\n if (showAllBtn) {\n showAllBtn.addEventListener('click', function() {\n hiddenComponents.classList.remove('hidden');\n showAllBtn.classList.add('hidden');\n showLessBtn.classList.remove('hidden');\n });\n }\n\n if (showLessBtn) {\n showLessBtn.addEventListener('click', function() {\n hiddenComponents.classList.add('hidden');\n showLessBtn.classList.add('hidden');\n showAllBtn.classList.remove('hidden');\n\n // Scroll back to visible components\n document.getElementById('visible-components').scrollIntoView({ behavior: 'smooth', block: 'nearest' });\n });\n }\n}\n\n// Initialize component tree on page load\ndocument.addEventListener('DOMContentLoaded', initializeComponentTree);\n\n"],"names":[],"sourceRoot":""}