/** * VocabularyDiscoveryItem - Progress item for vocabulary discovery (passive exposure) * Weight: 1 point * Prerequisites: None */ import ProgressItemInterface from '../interfaces/ProgressItemInterface.js'; class VocabularyDiscoveryItem extends ProgressItemInterface { constructor(word, data) { super( ProgressItemInterface.TYPES.VOCABULARY_DISCOVERY, `vocab-discover-${word}`, { word, ...data } ); this.word = word; this.data = data; } /** * Validate item data * @override */ validate() { if (!this.word || typeof this.word !== 'string') { throw new Error(`VocabularyDiscoveryItem: Invalid word - ${this.word}`); } if (!this.data) { throw new Error(`VocabularyDiscoveryItem: Missing data for word - ${this.word}`); } return true; } /** * Serialize to JSON * @override */ serialize() { return { ...this._getBaseSerialization(), word: this.word, data: this.data }; } /** * Get item weight * @override */ getWeight() { return ProgressItemInterface.WEIGHTS['vocabulary-discovery']; } /** * Check if can be completed (no prerequisites for discovery) * @override */ canComplete(userProgress) { // Discovery has no prerequisites - can always be completed // But shouldn't be completed if already discovered const discovered = userProgress.discoveredWords || []; return !discovered.includes(this.word); } } export default VocabularyDiscoveryItem;