11// EngineScript External Services Manager - ES6 Module
22// Handles external service status monitoring with drag-drop ordering and preferences
33
4- import { DashboardUtils } from '../modules/utils.js?v=2025.11.21.08 ' ;
5- import { SERVICE_DEFINITIONS } from './services-config.js?v=2025.11.21.08 ' ;
4+ import { DashboardUtils } from '../modules/utils.js?v=2025.11.21.09 ' ;
5+ import { SERVICE_DEFINITIONS } from './services-config.js?v=2025.11.21.09 ' ;
66
77export class ExternalServicesManager {
88 constructor ( containerSelector , settingsContainerSelector ) {
@@ -138,6 +138,9 @@ export class ExternalServicesManager {
138138
139139 this . container . appendChild ( categoryContainer ) ;
140140 }
141+
142+ // Enable drag and drop for service cards
143+ this . enableServiceDragDrop ( this . container ) ;
141144 } catch ( error ) {
142145 console . error ( 'Failed to load external services:' , error ) ;
143146 this . container . innerHTML = "" ;
@@ -267,7 +270,7 @@ export class ExternalServicesManager {
267270 categoryHeader . className = "category-header" ;
268271 categoryHeader . innerHTML = `
269272 <span>${ category } </span>
270- <button class="category-toggle-btn" data-category="${ category } ">
273+ <button class="category-toggle-all- btn" data-category="${ category } ">
271274 <span class="toggle-all-text">Toggle All</span>
272275 <i class="fas fa-toggle-on"></i>
273276 </button>
@@ -307,7 +310,7 @@ export class ExternalServicesManager {
307310 } ) ;
308311
309312 // Add toggle all button functionality
310- const toggleBtn = categoryHeader . querySelector ( ".category-toggle-btn" ) ;
313+ const toggleBtn = categoryHeader . querySelector ( ".category-toggle-all- btn" ) ;
311314 toggleBtn . addEventListener ( "click" , ( ) => {
312315 const allEnabled = categoryCheckboxes . every ( cb => cb . checked ) ;
313316 categoryCheckboxes . forEach ( cb => {
@@ -322,7 +325,7 @@ export class ExternalServicesManager {
322325
323326 // Save button
324327 const saveButton = document . createElement ( "button" ) ;
325- saveButton . className = "save- settings-btn" ;
328+ saveButton . className = "settings-save -btn" ;
326329 saveButton . innerHTML = '<i class="fas fa-save"></i> Save Changes' ;
327330 saveButton . disabled = true ;
328331
@@ -762,6 +765,94 @@ export class ExternalServicesManager {
762765 this . setCookie ( 'serviceOrder' , encodeURIComponent ( JSON . stringify ( orderArray ) ) , 365 ) ;
763766 }
764767
768+ // ============ Drag and Drop ============
769+
770+ /**
771+ * Enable drag-and-drop for service cards
772+ */
773+ enableServiceDragDrop ( container ) {
774+ const serviceCards = container . querySelectorAll ( '.external-service-card' ) ;
775+ let draggedElement = null ;
776+ let draggedServiceKey = null ;
777+
778+ serviceCards . forEach ( card => {
779+ card . draggable = true ;
780+
781+ card . addEventListener ( 'dragstart' , ( e ) => {
782+ draggedElement = card ;
783+ draggedServiceKey = card . dataset . serviceKey ;
784+ card . classList . add ( 'dragging' ) ;
785+ e . dataTransfer . effectAllowed = 'move' ;
786+ e . dataTransfer . setData ( 'text/html' , card . innerHTML ) ;
787+ } ) ;
788+
789+ card . addEventListener ( 'dragover' , ( e ) => {
790+ e . preventDefault ( ) ;
791+ e . dataTransfer . dropEffect = 'move' ;
792+
793+ const targetCard = e . target . closest ( '.external-service-card' ) ;
794+ if ( targetCard && targetCard !== draggedElement ) {
795+ targetCard . classList . add ( 'drag-over' ) ;
796+ }
797+ } ) ;
798+
799+ card . addEventListener ( 'dragenter' , ( e ) => {
800+ const targetCard = e . target . closest ( '.external-service-card' ) ;
801+ if ( targetCard && targetCard !== draggedElement ) {
802+ targetCard . classList . add ( 'drag-over' ) ;
803+ }
804+ } ) ;
805+
806+ card . addEventListener ( 'dragleave' , ( e ) => {
807+ const targetCard = e . target . closest ( '.external-service-card' ) ;
808+ if ( targetCard ) {
809+ targetCard . classList . remove ( 'drag-over' ) ;
810+ }
811+ } ) ;
812+
813+ card . addEventListener ( 'drop' , ( e ) => {
814+ e . preventDefault ( ) ;
815+
816+ const targetCard = e . target . closest ( '.external-service-card' ) ;
817+ if ( targetCard && targetCard !== draggedElement ) {
818+ targetCard . classList . remove ( 'drag-over' ) ;
819+
820+ // Swap positions
821+ const targetServiceKey = targetCard . dataset . serviceKey ;
822+ const allCards = Array . from ( container . querySelectorAll ( '.external-service-card' ) ) ;
823+ const draggedIndex = allCards . indexOf ( draggedElement ) ;
824+ const targetIndex = allCards . indexOf ( targetCard ) ;
825+
826+ if ( draggedIndex < targetIndex ) {
827+ targetCard . parentNode . insertBefore ( draggedElement , targetCard . nextSibling ) ;
828+ } else {
829+ targetCard . parentNode . insertBefore ( draggedElement , targetCard ) ;
830+ }
831+
832+ // Save new order
833+ this . saveCardOrder ( ) ;
834+ }
835+ } ) ;
836+
837+ card . addEventListener ( 'dragend' , ( ) => {
838+ card . classList . remove ( 'dragging' ) ;
839+ // Remove drag-over from all cards
840+ container . querySelectorAll ( '.external-service-card' ) . forEach ( c => {
841+ c . classList . remove ( 'drag-over' ) ;
842+ } ) ;
843+ } ) ;
844+ } ) ;
845+ }
846+
847+ /**
848+ * Save current card order to cookie
849+ */
850+ saveCardOrder ( ) {
851+ const cards = document . querySelectorAll ( '.external-service-card' ) ;
852+ const orderArray = Array . from ( cards ) . map ( card => card . dataset . serviceKey ) ;
853+ this . saveServiceOrder ( orderArray ) ;
854+ }
855+
765856 // ============ Notifications ============
766857
767858 /**
0 commit comments