import { Template } from 'meteor/templating'; import { ReactiveVar } from 'meteor/reactive-var'; import { Meteor } from 'meteor/meteor'; import { TAPi18n } from '/imports/i18n'; // Reactive variables for cron settings const migrationProgress = new ReactiveVar(0); const migrationStatus = new ReactiveVar(''); const migrationCurrentStep = new ReactiveVar(''); const migrationSteps = new ReactiveVar([]); const isMigrating = new ReactiveVar(false); const cronJobs = new ReactiveVar([]); Template.cronSettings.onCreated(function() { this.loading = new ReactiveVar(true); this.showMigrations = new ReactiveVar(true); this.showBoardOperations = new ReactiveVar(false); this.showJobs = new ReactiveVar(false); this.showAddJob = new ReactiveVar(false); // Board operations pagination this.currentPage = new ReactiveVar(1); this.pageSize = new ReactiveVar(20); this.searchTerm = new ReactiveVar(''); this.boardOperations = new ReactiveVar([]); this.operationStats = new ReactiveVar({}); this.pagination = new ReactiveVar({}); this.queueStats = new ReactiveVar({}); this.systemResources = new ReactiveVar({}); this.boardMigrationStats = new ReactiveVar({}); // Load initial data loadCronData(this); }); Template.cronSettings.helpers({ loading() { const instance = Template.instance(); return instance && instance.loading ? instance.loading.get() : true; }, showMigrations() { const instance = Template.instance(); return instance && instance.showMigrations ? instance.showMigrations.get() : true; }, showBoardOperations() { const instance = Template.instance(); return instance && instance.showBoardOperations ? instance.showBoardOperations.get() : false; }, showJobs() { const instance = Template.instance(); return instance && instance.showJobs ? instance.showJobs.get() : false; }, showAddJob() { const instance = Template.instance(); return instance && instance.showAddJob ? instance.showAddJob.get() : false; }, migrationProgress() { return migrationProgress.get(); }, migrationStatus() { return migrationStatus.get(); }, migrationCurrentStep() { return migrationCurrentStep.get(); }, migrationSteps() { const steps = migrationSteps.get(); const currentStep = migrationCurrentStep.get(); return steps.map(step => ({ ...step, isCurrentStep: step.name === currentStep })); }, cronJobs() { return cronJobs.get(); }, formatDate(date) { if (!date) return '-'; return new Date(date).toLocaleString(); }, boardOperations() { const instance = Template.instance(); return instance && instance.boardOperations ? instance.boardOperations.get() : []; }, operationStats() { const instance = Template.instance(); return instance && instance.operationStats ? instance.operationStats.get() : {}; }, pagination() { const instance = Template.instance(); return instance && instance.pagination ? instance.pagination.get() : {}; }, queueStats() { const instance = Template.instance(); return instance && instance.queueStats ? instance.queueStats.get() : {}; }, systemResources() { const instance = Template.instance(); return instance && instance.systemResources ? instance.systemResources.get() : {}; }, boardMigrationStats() { const instance = Template.instance(); return instance && instance.boardMigrationStats ? instance.boardMigrationStats.get() : {}; }, formatDateTime(date) { if (!date) return '-'; return new Date(date).toLocaleString(); }, formatDuration(startTime, endTime) { if (!startTime) return '-'; const start = new Date(startTime); const end = endTime ? new Date(endTime) : new Date(); const diffMs = end - start; const diffMins = Math.floor(diffMs / 60000); const diffSecs = Math.floor((diffMs % 60000) / 1000); if (diffMins > 0) { return `${diffMins}m ${diffSecs}s`; } else { return `${diffSecs}s`; } } }); Template.cronSettings.events({ 'click .js-cron-migrations'(event) { event.preventDefault(); const instance = Template.instance(); instance.showMigrations.set(true); instance.showJobs.set(false); instance.showAddJob.set(false); }, 'click .js-cron-board-operations'(event) { event.preventDefault(); const instance = Template.instance(); instance.showMigrations.set(false); instance.showBoardOperations.set(true); instance.showJobs.set(false); instance.showAddJob.set(false); loadBoardOperations(instance); }, 'click .js-cron-jobs'(event) { event.preventDefault(); const instance = Template.instance(); instance.showMigrations.set(false); instance.showBoardOperations.set(false); instance.showJobs.set(true); instance.showAddJob.set(false); loadCronJobs(instance); }, 'click .js-cron-add'(event) { event.preventDefault(); const instance = Template.instance(); instance.showMigrations.set(false); instance.showJobs.set(false); instance.showAddJob.set(true); }, 'click .js-start-all-migrations'(event) { event.preventDefault(); Meteor.call('cron.startAllMigrations', (error, result) => { if (error) { console.error('Failed to start migrations:', error); alert('Failed to start migrations: ' + error.message); } else { // Migrations started successfully pollMigrationProgress(Template.instance()); } }); }, 'click .js-pause-all-migrations'(event) { event.preventDefault(); // Pause all migration cron jobs const jobs = cronJobs.get(); jobs.forEach(job => { if (job.name.startsWith('migration_')) { Meteor.call('cron.pauseJob', job.name); } }); }, 'click .js-stop-all-migrations'(event) { event.preventDefault(); // Stop all migration cron jobs const jobs = cronJobs.get(); jobs.forEach(job => { if (job.name.startsWith('migration_')) { Meteor.call('cron.stopJob', job.name); } }); }, 'click .js-refresh-jobs'(event) { event.preventDefault(); loadCronJobs(Template.instance()); }, 'click .js-start-job'(event) { event.preventDefault(); const jobName = $(event.currentTarget).data('job'); Meteor.call('cron.startJob', jobName, (error, result) => { if (error) { console.error('Failed to start job:', error); alert('Failed to start job: ' + error.message); } else { console.log('Job started successfully'); loadCronJobs(Template.instance()); } }); }, 'click .js-pause-job'(event) { event.preventDefault(); const jobName = $(event.currentTarget).data('job'); Meteor.call('cron.pauseJob', jobName, (error, result) => { if (error) { console.error('Failed to pause job:', error); alert('Failed to pause job: ' + error.message); } else { console.log('Job paused successfully'); loadCronJobs(Template.instance()); } }); }, 'click .js-stop-job'(event) { event.preventDefault(); const jobName = $(event.currentTarget).data('job'); Meteor.call('cron.stopJob', jobName, (error, result) => { if (error) { console.error('Failed to stop job:', error); alert('Failed to stop job: ' + error.message); } else { console.log('Job stopped successfully'); loadCronJobs(Template.instance()); } }); }, 'click .js-remove-job'(event) { event.preventDefault(); const jobName = $(event.currentTarget).data('job'); if (confirm('Are you sure you want to remove this job?')) { Meteor.call('cron.removeJob', jobName, (error, result) => { if (error) { console.error('Failed to remove job:', error); alert('Failed to remove job: ' + error.message); } else { console.log('Job removed successfully'); loadCronJobs(Template.instance()); } }); } }, 'submit .js-add-cron-job-form'(event) { event.preventDefault(); const form = event.currentTarget; const formData = new FormData(form); const jobData = { name: formData.get('name'), description: formData.get('description'), schedule: formData.get('schedule'), weight: parseInt(formData.get('weight')) }; Meteor.call('cron.addJob', jobData, (error, result) => { if (error) { console.error('Failed to add job:', error); alert('Failed to add job: ' + error.message); } else { console.log('Job added successfully'); form.reset(); Template.instance().showJobs.set(true); Template.instance().showAddJob.set(false); loadCronJobs(Template.instance()); } }); }, 'click .js-cancel-add-job'(event) { event.preventDefault(); const instance = Template.instance(); instance.showJobs.set(true); instance.showAddJob.set(false); }, 'click .js-refresh-board-operations'(event) { event.preventDefault(); loadBoardOperations(Template.instance()); }, 'click .js-start-test-operation'(event) { event.preventDefault(); const testBoardId = 'test-board-' + Date.now(); const operationData = { sourceBoardId: 'source-board', targetBoardId: 'target-board', copyOptions: { includeCards: true, includeAttachments: true } }; Meteor.call('cron.startBoardOperation', testBoardId, 'copy_board', operationData, (error, result) => { if (error) { console.error('Failed to start test operation:', error); alert('Failed to start test operation: ' + error.message); } else { console.log('Test operation started:', result); loadBoardOperations(Template.instance()); } }); }, 'input .js-search-board-operations'(event) { const searchTerm = $(event.currentTarget).val(); const instance = Template.instance(); instance.searchTerm.set(searchTerm); instance.currentPage.set(1); loadBoardOperations(instance); }, 'click .js-prev-page'(event) { event.preventDefault(); const instance = Template.instance(); const currentPage = instance.currentPage.get(); if (currentPage > 1) { instance.currentPage.set(currentPage - 1); loadBoardOperations(instance); } }, 'click .js-next-page'(event) { event.preventDefault(); const instance = Template.instance(); const currentPage = instance.currentPage.get(); const pagination = instance.pagination.get(); if (currentPage < pagination.totalPages) { instance.currentPage.set(currentPage + 1); loadBoardOperations(instance); } }, 'click .js-pause-operation'(event) { event.preventDefault(); const operationId = $(event.currentTarget).data('operation'); // Implementation for pausing operation console.log('Pause operation:', operationId); }, 'click .js-resume-operation'(event) { event.preventDefault(); const operationId = $(event.currentTarget).data('operation'); // Implementation for resuming operation console.log('Resume operation:', operationId); }, 'click .js-stop-operation'(event) { event.preventDefault(); const operationId = $(event.currentTarget).data('operation'); if (confirm('Are you sure you want to stop this operation?')) { // Implementation for stopping operation console.log('Stop operation:', operationId); } }, 'click .js-view-details'(event) { event.preventDefault(); const operationId = $(event.currentTarget).data('operation'); // Implementation for viewing operation details console.log('View details for operation:', operationId); }, 'click .js-force-board-scan'(event) { event.preventDefault(); Meteor.call('cron.forceBoardMigrationScan', (error, result) => { if (error) { console.error('Failed to force board scan:', error); alert('Failed to force board scan: ' + error.message); } else { // Board scan started successfully // Refresh the data loadBoardOperations(Template.instance()); } }); } }); // Helper functions for cron settings function loadCronData(instance) { instance.loading.set(true); // Load migration progress Meteor.call('cron.getMigrationProgress', (error, result) => { if (result) { migrationProgress.set(result.progress); migrationStatus.set(result.status); migrationCurrentStep.set(result.currentStep); migrationSteps.set(result.steps); isMigrating.set(result.isMigrating); } }); // Load cron jobs loadCronJobs(instance); instance.loading.set(false); } function loadCronJobs(instance) { Meteor.call('cron.getJobs', (error, result) => { if (result) { cronJobs.set(result); } }); } function loadBoardOperations(instance) { const page = instance.currentPage.get(); const limit = instance.pageSize.get(); const searchTerm = instance.searchTerm.get(); Meteor.call('cron.getAllBoardOperations', page, limit, searchTerm, (error, result) => { if (result) { instance.boardOperations.set(result.operations); instance.pagination.set({ total: result.total, page: result.page, limit: result.limit, totalPages: result.totalPages, start: ((result.page - 1) * result.limit) + 1, end: Math.min(result.page * result.limit, result.total), hasPrev: result.page > 1, hasNext: result.page < result.totalPages }); } }); // Load operation stats Meteor.call('cron.getBoardOperationStats', (error, result) => { if (result) { instance.operationStats.set(result); } }); // Load queue stats Meteor.call('cron.getQueueStats', (error, result) => { if (result) { instance.queueStats.set(result); } }); // Load system resources Meteor.call('cron.getSystemResources', (error, result) => { if (result) { instance.systemResources.set(result); } }); // Load board migration stats Meteor.call('cron.getBoardMigrationStats', (error, result) => { if (result) { instance.boardMigrationStats.set(result); } }); } function pollMigrationProgress(instance) { const pollInterval = setInterval(() => { Meteor.call('cron.getMigrationProgress', (error, result) => { if (result) { migrationProgress.set(result.progress); migrationStatus.set(result.status); migrationCurrentStep.set(result.currentStep); migrationSteps.set(result.steps); isMigrating.set(result.isMigrating); // Stop polling if migration is complete if (!result.isMigrating && result.progress === 100) { clearInterval(pollInterval); } } }); }, 1000); }