import { BlacklistStorageManager } from './BlacklistStorageManager.js'; import BrandLogoRegistry from './BrandLogoRegistry.js'; /** * BlacklistPanelManager - Manages the Blacklist Panel UI and functionality * * Provides UI for adding/removing brands from the blacklist and displays * the current list of blacklisted brands with their logos. * * Requirements: 2.1, 2.4, 3.1, 3.2, 3.3, 3.4 */ export class BlacklistPanelManager { constructor(blacklistStorage = null, logoRegistry = null) { this.blacklistStorage = blacklistStorage || new BlacklistStorageManager(); this.logoRegistry = logoRegistry || new BrandLogoRegistry(); this.container = null; this.eventBus = null; // Initialize event bus connection this.initializeEventBus(); } /** * Initializes connection to the global event bus */ initializeEventBus() { if (typeof window !== 'undefined' && window.amazonExtEventBus) { this.eventBus = window.amazonExtEventBus; this.setupEventListeners(); } else { setTimeout(() => { if (typeof window !== 'undefined' && window.amazonExtEventBus) { this.eventBus = window.amazonExtEventBus; this.setupEventListeners(); } }, 100); } } /** * Sets up event listeners for blacklist updates */ setupEventListeners() { if (!this.eventBus) return; this.eventBus.on('blacklist:updated', () => { this.loadBrands(); }); } /** * Emits an event through the event bus * @param {string} eventName - Event name * @param {*} data - Event data */ emitEvent(eventName, data) { if (this.eventBus) { this.eventBus.emit(eventName, data); } } /** * Creates the Blacklist Panel content HTML structure * Requirement 2.1: Display input field for brand names * @returns {HTMLElement} */ createBlacklistContent() { const container = document.createElement('div'); container.className = 'amazon-ext-blacklist-content'; container.innerHTML = `

Blacklist

Markennamen hinzufügen, um Produkte zu markieren

`; this.container = container; this.attachEventListeners(); this.loadBrands(); return container; } /** * Attaches event listeners to the Blacklist Panel elements */ attachEventListeners() { if (!this.container) return; const input = this.container.querySelector('.brand-input'); const addBtn = this.container.querySelector('.add-brand-btn'); if (addBtn) { addBtn.addEventListener('click', () => this.handleAddBrand()); } if (input) { input.addEventListener('keypress', (e) => { if (e.key === 'Enter') { this.handleAddBrand(); } }); } } /** * Handles adding a new brand to the blacklist * Requirements: 2.2, 2.4, 2.5, 2.6 */ async handleAddBrand() { if (!this.container) return; const input = this.container.querySelector('.brand-input'); const brandName = input ? input.value.trim() : ''; // Clear previous messages this.clearMessage(); if (!brandName) { this.showMessage('Bitte einen Markennamen eingeben', 'error'); if (input) input.focus(); return; } try { await this.blacklistStorage.addBrand(brandName); // Requirement 2.4: Clear input field after saving if (input) { input.value = ''; } this.showMessage(`"${brandName}" zur Blacklist hinzugefügt`, 'success'); this.loadBrands(); } catch (error) { // Requirement 2.5: Display message for duplicate entry if (error.message === 'Brand already exists') { this.showMessage('Diese Marke ist bereits in der Blacklist', 'error'); } else if (error.message.includes('quota')) { this.showMessage('Speicher voll. Bitte löschen Sie einige Marken.', 'error'); } else { this.showMessage('Fehler beim Speichern', 'error'); } if (input) input.focus(); } } /** * Loads and renders all blacklisted brands * Requirement 3.1: Display all saved brand names in a list */ async loadBrands() { if (!this.container) return; try { const brands = await this.blacklistStorage.getBrands(); this.renderBrandList(brands); } catch (error) { console.error('Error loading brands:', error); this.showMessage('Fehler beim Laden der Marken', 'error'); } } /** * Renders the brand list in the Blacklist Panel * Requirements: 3.1, 3.2, 3.3 * @param {Array} brands - Array of blacklisted brands */ renderBrandList(brands) { if (!this.container) return; const listContainer = this.container.querySelector('.brand-list'); if (!listContainer) return; if (!brands || brands.length === 0) { listContainer.innerHTML = '

Keine Marken in der Blacklist

'; return; } // Requirement 3.1: Display all saved brand names // Requirement 3.2: Display each brand with its logo // Requirement 3.3: Provide delete button for each brand listContainer.innerHTML = brands.map(brand => `
${this.escapeHtml(brand.name)}
`).join(''); // Attach delete button event listeners this.attachDeleteListeners(listContainer); } /** * Attaches event listeners to delete buttons * @param {HTMLElement} listContainer - Brand list container element */ attachDeleteListeners(listContainer) { const deleteButtons = listContainer.querySelectorAll('.delete-brand-btn'); deleteButtons.forEach(btn => { btn.addEventListener('click', (e) => { const brandId = e.target.dataset.id; if (brandId) { this.handleDeleteBrand(brandId); } }); }); } /** * Handles deleting a brand from the blacklist * Requirement 3.4: Remove brand from storage and update display * @param {string} brandId - Brand ID to delete */ async handleDeleteBrand(brandId) { try { const brands = await this.blacklistStorage.getBrands(); const brand = brands.find(b => b.id === brandId); const brandName = brand ? brand.name : ''; await this.blacklistStorage.deleteBrand(brandId); if (brandName) { this.showMessage(`"${brandName}" entfernt`, 'success'); } this.loadBrands(); } catch (error) { console.error('Error deleting brand:', error); this.showMessage('Fehler beim Löschen der Marke', 'error'); } } /** * Shows a feedback message (success or error) * @param {string} text - Message text * @param {string} type - Message type ('success' or 'error') */ showMessage(text, type) { if (!this.container) return; const messageEl = this.container.querySelector('.blacklist-message'); if (!messageEl) return; messageEl.textContent = text; messageEl.className = `blacklist-message ${type}`; messageEl.style.display = 'block'; // Auto-hide after 3 seconds setTimeout(() => { if (messageEl) { messageEl.style.display = 'none'; } }, 3000); } /** * Clears the feedback message */ clearMessage() { if (!this.container) return; const messageEl = this.container.querySelector('.blacklist-message'); if (messageEl) { messageEl.style.display = 'none'; messageEl.textContent = ''; } } /** * Shows the Blacklist Panel (called when menu item is clicked) */ showBlacklistPanel() { this.loadBrands(); } /** * Hides the Blacklist Panel (called when menu is closed) */ hideBlacklistPanel() { // Cleanup if needed } /** * Escapes HTML special characters to prevent XSS * @param {string} text - Text to escape * @returns {string} Escaped text */ escapeHtml(text) { const div = document.createElement('div'); div.textContent = text; return div.innerHTML; } }