/** * Master Trainer Events Management Page Object * Handles interactions with the master trainer events management page */ const BasePage = require('../base/BasePage'); class MasterTrainerEvents extends BasePage { constructor(page = null) { super(page); this.url = '/master-trainer/events/'; this.title = 'Events Management'; this.selectors = { // Main page elements pageTitle: 'h1, .page-title, .events-title', eventsContainer: '.events-container, .events-list, .events-table', // Events list and table eventsTable: 'table.events-table, .events-grid', eventRows: 'tr.event-row, .event-item', eventTitle: '.event-title, .event-name', eventDate: '.event-date, .event-start-date', eventStatus: '.event-status, .status', eventTrainer: '.event-trainer, .trainer-name', eventVenue: '.event-venue, .venue-name', // Action buttons createEventBtn: 'a[href*="create"], .create-event, .add-event', editEventBtn: 'a[href*="edit"], .edit-event, .event-edit', deleteEventBtn: 'a[href*="delete"], .delete-event, .event-delete', viewEventBtn: 'a[href*="view"], .view-event, .event-view', // Bulk actions bulkActionsSelect: 'select[name="bulk-action"], .bulk-actions select', bulkApplyBtn: '.bulk-apply, .apply-bulk-action', selectAllCheckbox: 'input[type="checkbox"].select-all', eventCheckboxes: 'input[type="checkbox"].event-select', // Filters and search searchInput: 'input[type="search"], .search-events, #event-search', searchBtn: '.search-btn, .search-submit', statusFilter: 'select.status-filter, #status-filter', trainerFilter: 'select.trainer-filter, #trainer-filter', dateFilter: 'input.date-filter, #date-filter', venueFilter: 'select.venue-filter, #venue-filter', clearFiltersBtn: '.clear-filters, .reset-filters', // Event statistics and overview eventStats: '.event-stats, .events-statistics', totalEvents: '.total-events, [data-metric="total"]', upcomingEvents: '.upcoming-events, [data-metric="upcoming"]', pastEvents: '.past-events, [data-metric="past"]', cancelledEvents: '.cancelled-events, [data-metric="cancelled"]', // Event categories and types categoryFilter: 'select.category-filter, #category-filter', eventTypes: '.event-types, .type-filter', // Pagination pagination: '.pagination, .page-navigation', prevPageBtn: '.prev-page, .previous', nextPageBtn: '.next-page, .next', pageNumbers: '.page-numbers a', // Export and reporting exportBtn: '.export-events, .download-events', reportingBtn: '.event-reports, .reporting', // Loading and status indicators loadingIndicator: '.loading, .spinner', errorMessage: '.error-message, .notice-error', successMessage: '.success-message, .notice-success', emptyState: '.no-events, .events-empty' }; } /** * Wait for events page to be fully loaded */ async waitForPageReady() { await this.waitForElement(this.selectors.pageTitle); await this.waitForAjaxComplete(); // Wait for events container or empty state try { await Promise.race([ this.waitForElement(this.selectors.eventsContainer, 10000), this.waitForElement(this.selectors.emptyState, 10000) ]); } catch (error) { console.warn('Events container or empty state not found, continuing...'); } } /** * Get all events from the current page */ async getEventsList() { const events = []; const eventRows = await this.page.$$(this.selectors.eventRows); for (const row of eventRows) { const event = { title: await this.getTextFromElement(row, this.selectors.eventTitle), date: await this.getTextFromElement(row, this.selectors.eventDate), status: await this.getTextFromElement(row, this.selectors.eventStatus), trainer: await this.getTextFromElement(row, this.selectors.eventTrainer), venue: await this.getTextFromElement(row, this.selectors.eventVenue) }; events.push(event); } return events; } /** * Get event statistics from the page */ async getEventStatistics() { const stats = {}; try { if (await this.hasElement(this.selectors.totalEvents)) { const totalText = await this.getElementText(this.selectors.totalEvents); stats.total = this.extractNumber(totalText); } if (await this.hasElement(this.selectors.upcomingEvents)) { const upcomingText = await this.getElementText(this.selectors.upcomingEvents); stats.upcoming = this.extractNumber(upcomingText); } if (await this.hasElement(this.selectors.pastEvents)) { const pastText = await this.getElementText(this.selectors.pastEvents); stats.past = this.extractNumber(pastText); } if (await this.hasElement(this.selectors.cancelledEvents)) { const cancelledText = await this.getElementText(this.selectors.cancelledEvents); stats.cancelled = this.extractNumber(cancelledText); } } catch (error) { console.warn('Failed to get event statistics:', error.message); } return stats; } /** * Search for events by term */ async searchEvents(searchTerm) { if (await this.hasElement(this.selectors.searchInput)) { await this.fillField(this.selectors.searchInput, searchTerm); if (await this.hasElement(this.selectors.searchBtn)) { await this.clickElement(this.selectors.searchBtn); } else { // Submit by pressing Enter await this.page.keyboard.press('Enter'); } await this.waitForAjaxComplete(); await this.waitForPageReady(); } } /** * Filter events by status */ async filterByStatus(status) { if (await this.hasElement(this.selectors.statusFilter)) { await this.selectOption(this.selectors.statusFilter, status); await this.waitForAjaxComplete(); await this.waitForPageReady(); } } /** * Filter events by trainer */ async filterByTrainer(trainerId) { if (await this.hasElement(this.selectors.trainerFilter)) { await this.selectOption(this.selectors.trainerFilter, trainerId); await this.waitForAjaxComplete(); await this.waitForPageReady(); } } /** * Clear all filters */ async clearAllFilters() { if (await this.hasElement(this.selectors.clearFiltersBtn)) { await this.clickElement(this.selectors.clearFiltersBtn); await this.waitForAjaxComplete(); await this.waitForPageReady(); } } /** * Navigate to create event page */ async createNewEvent() { if (await this.hasElement(this.selectors.createEventBtn)) { await this.clickElement(this.selectors.createEventBtn); await this.waitForPageLoad(); return true; } return false; } /** * Edit an event by index or title */ async editEvent(identifier) { const eventRows = await this.page.$$(this.selectors.eventRows); if (typeof identifier === 'number' && eventRows[identifier]) { // Edit by index const editBtn = await eventRows[identifier].$(this.selectors.editEventBtn); if (editBtn) { await editBtn.click(); await this.waitForPageLoad(); return true; } } else if (typeof identifier === 'string') { // Edit by event title for (const row of eventRows) { const titleElement = await row.$(this.selectors.eventTitle); if (titleElement) { const title = await titleElement.textContent(); if (title.trim().includes(identifier)) { const editBtn = await row.$(this.selectors.editEventBtn); if (editBtn) { await editBtn.click(); await this.waitForPageLoad(); return true; } } } } } return false; } /** * Perform bulk actions on selected events */ async performBulkAction(action, eventIndices = []) { // Select events by index const eventRows = await this.page.$$(this.selectors.eventRows); for (const index of eventIndices) { if (eventRows[index]) { const checkbox = await eventRows[index].$(this.selectors.eventCheckboxes); if (checkbox) { await checkbox.click(); } } } // Select bulk action if (await this.hasElement(this.selectors.bulkActionsSelect)) { await this.selectOption(this.selectors.bulkActionsSelect, action); if (await this.hasElement(this.selectors.bulkApplyBtn)) { await this.clickElement(this.selectors.bulkApplyBtn); await this.waitForAjaxComplete(); await this.waitForPageReady(); return true; } } return false; } /** * Select all events on current page */ async selectAllEvents() { if (await this.hasElement(this.selectors.selectAllCheckbox)) { await this.clickElement(this.selectors.selectAllCheckbox); return true; } return false; } /** * Export events data */ async exportEvents() { if (await this.hasElement(this.selectors.exportBtn)) { await this.clickElement(this.selectors.exportBtn); // Wait for download to start await this.page.waitForTimeout(2000); return true; } return false; } /** * Check if events page has any events */ async hasEvents() { return await this.hasElement(this.selectors.eventRows); } /** * Check if page is showing empty state */ async isEmptyState() { return await this.isElementVisible(this.selectors.emptyState); } /** * Get available event management features */ async getAvailableFeatures() { return { canCreate: await this.hasElement(this.selectors.createEventBtn), canSearch: await this.hasElement(this.selectors.searchInput), canFilter: await this.hasElement(this.selectors.statusFilter), canBulkAction: await this.hasElement(this.selectors.bulkActionsSelect), canExport: await this.hasElement(this.selectors.exportBtn), hasStatistics: await this.hasElement(this.selectors.eventStats) }; } /** * Helper method to get text from child element */ async getTextFromElement(parentElement, selector) { try { const element = await parentElement.$(selector); return element ? await element.textContent() : ''; } catch (error) { return ''; } } /** * Extract number from text string */ extractNumber(text) { const match = text.match(/\d+/); return match ? parseInt(match[0], 10) : 0; } } module.exports = MasterTrainerEvents;