| |
| |
| |
| |
|
|
| class TopcoderChallengeApp { |
| constructor() { |
| |
| this.ui = new UIComponents(); |
| this.userManager = new UserManager(apiClient, this.ui); |
| this.challengeManager = new ChallengeManager(apiClient, this.ui); |
| |
| |
| this.currentUserData = null; |
| |
| |
| this.configSection = document.getElementById('configSection'); |
| this.resultsSection = document.getElementById('resultsSection'); |
| this.configForm = document.getElementById('configForm'); |
| this.emailInput = document.getElementById('email'); |
| this.preferencesInput = document.getElementById('preferences'); |
| this.displayEmail = document.getElementById('displayEmail'); |
| this.displayPreferences = document.getElementById('displayPreferences'); |
| |
| |
| this.init(); |
| } |
|
|
| |
| |
| |
| init() { |
| this.setupEventListeners(); |
| this.setupKeyboardShortcuts(); |
| this.setupFormEnhancements(); |
| |
| |
| this.checkPrefilledEmail(); |
| |
| console.log('Topcoder Challenge Steward Agent - UI Ready!'); |
| } |
|
|
| |
| |
| |
| setupEventListeners() { |
| |
| this.configForm?.addEventListener('submit', (e) => this.handleFormSubmit(e)); |
| |
| |
| this.emailInput?.addEventListener('blur', () => this.handleEmailChange()); |
| this.emailInput?.addEventListener('input', this.ui.debounce(() => this.handleEmailInput(), 1200)); |
| |
| |
| this.setupButtonHandlers(); |
| } |
|
|
| |
| |
| |
| setupButtonHandlers() { |
| const buttons = { |
| 'editPreferences': () => this.editPreferences(), |
| 'registerForNotifications': () => this.registerForNotifications(), |
| 'refreshChallenges': () => this.refreshChallenges(), |
| 'newSearch': () => this.newSearch() |
| }; |
|
|
| Object.entries(buttons).forEach(([id, handler]) => { |
| const element = document.getElementById(id); |
| if (element) { |
| element.addEventListener('click', handler); |
| } |
| }); |
| } |
|
|
|
|
| |
| |
| |
| setupKeyboardShortcuts() { |
| document.addEventListener('keydown', (e) => { |
| |
| if (e.key === 'Enter' && e.target === this.preferencesInput && !e.shiftKey) { |
| e.preventDefault(); |
| this.configForm?.dispatchEvent(new Event('submit')); |
| } |
| }); |
| } |
|
|
| |
| |
| |
| setupFormEnhancements() { |
| |
| this.preferencesInput?.addEventListener('input', () => { |
| this.ui.autoResizeTextarea(this.preferencesInput); |
| }); |
| } |
|
|
| |
| |
| |
| async handleFormSubmit(e) { |
| e.preventDefault(); |
| |
| const email = this.emailInput?.value.trim(); |
| const preferences = this.preferencesInput?.value.trim(); |
| |
| |
| if (!this.validateForm()) { |
| return; |
| } |
| |
| |
| this.currentUserData = { email, preferences }; |
| |
| try { |
| |
| await this.challengeManager.fetchAndDisplayChallenges(preferences); |
| |
| |
| await this.checkUserStatus(); |
| |
| |
| this.showResults(); |
| |
| } catch (error) { |
| console.error('Error in form submission:', error); |
| this.ui.showError(`Failed to fetch challenges: ${error.message}`); |
| } |
| } |
|
|
| |
| |
| |
| validateForm() { |
| const emailValid = this.ui.validateField(this.emailInput, { |
| required: true, |
| email: true, |
| name: 'Email' |
| }); |
| |
| return emailValid; |
| } |
|
|
| |
| |
| |
| async handleEmailInput() { |
| const email = this.emailInput?.value.trim(); |
| |
| if (email && this.userManager.validateEmail(email)) { |
| await this.fetchExistingUserPreferencesQuiet(email); |
| } else { |
| this.userManager.removeUserStatusDisplay(); |
| } |
| } |
|
|
| |
| |
| |
| async handleEmailChange() { |
| const email = this.emailInput?.value.trim(); |
| |
| if (email && this.userManager.validateEmail(email)) { |
| await this.fetchExistingUserPreferences(email); |
| } |
| } |
|
|
| |
| |
| |
| async fetchExistingUserPreferences(email) { |
| try { |
| |
| this.ui.showLoading('Loading user data...'); |
| |
| const user = await this.userManager.autoFillPreferences(email, this.preferencesInput); |
| |
| if (user) { |
| this.showUserStatusInForm(user); |
| this.ui.showInfoMessage('Found existing account for this email address'); |
| } else { |
| this.userManager.removeUserStatusDisplay(); |
| } |
| |
| } catch (error) { |
| console.log('Could not fetch existing user preferences:', error.message); |
| this.userManager.removeUserStatusDisplay(); |
| } finally { |
| |
| this.ui.hideLoading(); |
| } |
| } |
|
|
| |
| |
| |
| showUserStatusInForm(user) { |
| |
| this.userManager.removeUserStatusDisplay(); |
| |
| |
| const statusContainer = this.userManager.createUserStatusDisplay( |
| user, |
| () => this.handleDeactivateFromForm(), |
| () => this.handleReactivateFromForm() |
| ); |
| |
| |
| this.configForm?.parentNode?.insertBefore(statusContainer, this.configForm.nextSibling); |
| } |
|
|
| |
| |
| |
| async handleDeactivateFromForm() { |
| const email = this.emailInput?.value.trim(); |
| if (!email) { |
| this.ui.showError('Email is required'); |
| return; |
| } |
| |
| try { |
| |
| this.ui.showLoading('Deactivating notifications...'); |
| |
| await this.userManager.deactivateUser(email); |
| |
| |
| const user = await this.userManager.autoFillPreferences(email, this.preferencesInput); |
| if (user) { |
| this.showUserStatusInForm(user); |
| } else { |
| this.userManager.removeUserStatusDisplay(); |
| } |
| } catch (error) { |
| |
| } finally { |
| this.ui.hideLoading(); |
| } |
| } |
|
|
| |
| |
| |
| async handleReactivateFromForm() { |
| const email = this.emailInput?.value.trim(); |
| if (!email) { |
| this.ui.showError('Email is required'); |
| return; |
| } |
| |
| try { |
| |
| this.ui.showLoading('Reactivating notifications...'); |
| |
| await this.userManager.reactivateUser(email); |
| |
| |
| const user = await this.userManager.autoFillPreferences(email, this.preferencesInput); |
| if (user) { |
| this.showUserStatusInForm(user); |
| } else { |
| this.userManager.removeUserStatusDisplay(); |
| } |
| } catch (error) { |
| |
| } finally { |
| this.ui.hideLoading(); |
| } |
| } |
|
|
| |
| |
| |
| showResults() { |
| if (this.currentUserData) { |
| |
| if (this.displayEmail) { |
| this.displayEmail.textContent = this.currentUserData.email; |
| } |
| if (this.displayPreferences) { |
| this.displayPreferences.textContent = this.currentUserData.preferences || 'No specific preferences'; |
| } |
| } |
| |
| |
| if (this.configSection) { |
| this.configSection.style.display = 'none'; |
| } |
| if (this.resultsSection) { |
| this.resultsSection.style.display = 'block'; |
| } |
| } |
|
|
| |
| |
| |
| editPreferences() { |
| if (this.currentUserData) { |
| |
| if (this.emailInput) { |
| this.emailInput.value = this.currentUserData.email; |
| } |
| if (this.preferencesInput) { |
| this.preferencesInput.value = this.currentUserData.preferences; |
| this.ui.autoResizeTextarea(this.preferencesInput); |
| } |
| } |
| |
| |
| if (this.configSection) { |
| this.configSection.style.display = 'block'; |
| } |
| if (this.resultsSection) { |
| this.resultsSection.style.display = 'none'; |
| } |
| |
| |
| this.preferencesInput?.focus(); |
| } |
|
|
| |
| |
| |
| async refreshChallenges() { |
| if (!this.currentUserData) { |
| this.ui.showError('No current search to refresh'); |
| return; |
| } |
| |
| try { |
| await this.challengeManager.fetchAndDisplayChallenges(this.currentUserData.preferences); |
| this.ui.showSuccessMessage('Challenges refreshed successfully!'); |
| } catch (error) { |
| console.error('Error refreshing challenges:', error); |
| this.ui.showError(`Failed to refresh challenges: ${error.message}`); |
| } |
| } |
|
|
| |
| |
| |
| newSearch() { |
| |
| if (this.emailInput) { |
| this.emailInput.value = ''; |
| } |
| if (this.preferencesInput) { |
| this.preferencesInput.value = ''; |
| } |
| this.currentUserData = null; |
| |
| |
| this.challengeManager.clearChallenges(); |
| |
| |
| this.userManager.removeUserStatusDisplay(); |
| |
| |
| if (this.configSection) { |
| this.configSection.style.display = 'block'; |
| } |
| if (this.resultsSection) { |
| this.resultsSection.style.display = 'none'; |
| } |
| |
| |
| this.emailInput?.focus(); |
| } |
|
|
| |
| |
| |
| async registerForNotifications() { |
| if (!this.currentUserData) { |
| this.ui.showError('No user data available'); |
| return; |
| } |
| |
| try { |
| |
| this.ui.showLoading('Registering for daily notifications...'); |
| |
| await this.userManager.registerOrUpdateUser( |
| this.currentUserData.email, |
| this.currentUserData.preferences |
| ); |
| |
| |
| this.updateScheduleButtonState(true); |
| |
| } catch (error) { |
| console.error('Error registering for notifications:', error); |
| this.ui.showError(`Failed to register for notifications: ${error.message}`); |
| } finally { |
| this.ui.hideLoading(); |
| } |
| } |
|
|
| |
| |
| |
| async checkUserStatus() { |
| if (!this.currentUserData || !this.currentUserData.email) { |
| return; |
| } |
| |
| try { |
| const user = await this.userManager.loadUserByEmail(this.currentUserData.email); |
| |
| if (user && user.active) { |
| this.updateScheduleButtonState(true); |
| } else { |
| this.updateScheduleButtonState(false); |
| } |
| |
| } catch (error) { |
| console.log('Could not check user status:', error.message); |
| } |
| } |
|
|
| |
| |
| |
| updateScheduleButtonState(isActive) { |
| const registerBtn = document.getElementById('registerForNotifications'); |
| const unregisterBtn = document.getElementById('unregisterBtn'); |
| |
| if (registerBtn) { |
| if (isActive) { |
| registerBtn.innerHTML = '<i class="fas fa-check"></i> Registered for Daily Notifications'; |
| registerBtn.classList.add('btn-success'); |
| registerBtn.classList.remove('btn-primary'); |
| registerBtn.disabled = true; |
| |
| |
| if (!unregisterBtn) { |
| this.addUnregisterButton(); |
| } |
| } else { |
| registerBtn.innerHTML = '<i class="fas fa-bell"></i> Register for Daily Notifications'; |
| registerBtn.classList.remove('btn-success'); |
| registerBtn.classList.add('btn-primary'); |
| registerBtn.disabled = false; |
| |
| |
| if (unregisterBtn) { |
| unregisterBtn.remove(); |
| } |
| } |
| } |
| } |
|
|
| |
| |
| |
| addUnregisterButton() { |
| const existingBtn = document.getElementById('unregisterBtn'); |
| if (existingBtn) { |
| return; |
| } |
| |
| const unregisterBtn = document.createElement('button'); |
| unregisterBtn.id = 'unregisterBtn'; |
| unregisterBtn.className = 'btn btn-danger'; |
| unregisterBtn.innerHTML = '<i class="fas fa-times"></i> Deactivate'; |
| unregisterBtn.style.marginLeft = '0.5rem'; |
| |
| |
| unregisterBtn.addEventListener('click', () => this.handleUnregister()); |
| |
| |
| const resultsActions = document.querySelector('.results-actions'); |
| if (resultsActions) { |
| resultsActions.appendChild(unregisterBtn); |
| } |
| } |
|
|
| |
| |
| |
| async handleUnregister() { |
| if (!this.currentUserData || !this.currentUserData.email) { |
| this.ui.showError('No user data available'); |
| return; |
| } |
| |
| try { |
| await this.userManager.deactivateUser(this.currentUserData.email); |
| this.updateScheduleButtonState(false); |
| this.userManager.removeUserStatusDisplay(); |
| } catch (error) { |
| |
| } |
| } |
|
|
| |
| |
| |
| async checkPrefilledEmail() { |
| |
| setTimeout(async () => { |
| const email = this.emailInput?.value.trim(); |
| |
| if (email && this.userManager.validateEmail(email)) { |
| console.log('Found prefilled email on page load:', email); |
| await this.fetchExistingUserPreferencesWithLoading(email); |
| } |
| }, 100); |
| } |
|
|
| |
| |
| |
| async fetchExistingUserPreferencesWithLoading(email) { |
| try { |
| |
| this.ui.showLoading('Loading user data...'); |
| |
| const user = await this.userManager.autoFillPreferences(email, this.preferencesInput); |
| |
| if (user) { |
| this.showUserStatusInForm(user); |
| this.ui.showInfoMessage('Found existing account for this email address'); |
| } else { |
| this.userManager.removeUserStatusDisplay(); |
| } |
| |
| } catch (error) { |
| console.log('Could not fetch existing user preferences:', error.message); |
| this.userManager.removeUserStatusDisplay(); |
| } finally { |
| |
| this.ui.hideLoading(); |
| } |
| } |
|
|
| |
| |
| |
| async fetchExistingUserPreferencesQuiet(email) { |
| try { |
| const user = await this.userManager.autoFillPreferences(email, this.preferencesInput); |
| |
| if (user) { |
| this.showUserStatusInForm(user); |
| |
| } else { |
| this.userManager.removeUserStatusDisplay(); |
| } |
| |
| } catch (error) { |
| console.log('Could not fetch existing user preferences:', error.message); |
| this.userManager.removeUserStatusDisplay(); |
| } |
| } |
| } |
|
|
| |
| document.addEventListener('DOMContentLoaded', function() { |
| new TopcoderChallengeApp(); |
| }); |
|
|