const database = require('../config/database'); const { v4: uuidv4 } = require('uuid'); class Feature { constructor(data = {}) { this.id = data.id; this.template_id = data.template_id; this.feature_id = data.feature_id; this.name = data.name; this.description = data.description; this.feature_type = data.feature_type; this.complexity = data.complexity; this.display_order = data.display_order; this.usage_count = data.usage_count; this.user_rating = data.user_rating; this.is_default = data.is_default; this.created_by_user = data.created_by_user; this.created_at = data.created_at; this.updated_at = data.updated_at; } // Update feature fields static async update(id, updateData) { const fields = [] const values = [] let idx = 1 const allowed = [ 'name', 'description', 'feature_type', 'complexity', 'display_order', 'is_default' ] for (const key of allowed) { if (updateData[key] !== undefined) { fields.push(`${key} = $${idx++}`) values.push(updateData[key]) } } if (fields.length === 0) { return await Feature.getById(id) } const query = ` UPDATE template_features SET ${fields.join(', ')}, updated_at = NOW() WHERE id = $${idx} RETURNING * ` values.push(id) const result = await database.query(query, values) return result.rows.length > 0 ? new Feature(result.rows[0]) : null } // Delete a feature static async delete(id) { const result = await database.query('DELETE FROM template_features WHERE id = $1', [id]) return result.rowCount > 0 } // Get all features for a template static async getByTemplateId(templateId) { const query = ` SELECT * FROM template_features WHERE template_id = $1 ORDER BY CASE feature_type WHEN 'essential' THEN 1 WHEN 'suggested' THEN 2 WHEN 'custom' THEN 3 END, display_order, usage_count DESC, name `; const result = await database.query(query, [templateId]); return result.rows.map(row => new Feature(row)); } // Get popular features across all templates static async getPopularFeatures(limit = 10) { const query = ` SELECT tf.*, t.title as template_title, t.type as template_type FROM template_features tf JOIN templates t ON tf.template_id = t.id WHERE tf.usage_count > 0 ORDER BY tf.usage_count DESC, tf.user_rating DESC LIMIT $1 `; const result = await database.query(query, [limit]); return result.rows.map(row => new Feature(row)); } // Create new feature static async create(featureData) { const id = uuidv4(); const query = ` INSERT INTO template_features ( id, template_id, feature_id, name, description, feature_type, complexity, display_order, is_default, created_by_user ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10) RETURNING * `; const values = [ id, featureData.template_id, featureData.feature_id, featureData.name, featureData.description, featureData.feature_type, featureData.complexity, featureData.display_order || 0, featureData.is_default || false, featureData.created_by_user || false ]; const result = await database.query(query, values); return new Feature(result.rows[0]); } // Increment usage count async incrementUsage(userSession = null, projectId = null) { const client = await database.getClient(); try { await client.query('BEGIN'); // Update usage count const updateQuery = ` UPDATE template_features SET usage_count = usage_count + 1 WHERE id = $1 RETURNING * `; const updateResult = await client.query(updateQuery, [this.id]); // Track usage const trackQuery = ` INSERT INTO feature_usage (template_id, feature_id, user_session, project_id) VALUES ($1, $2, $3, $4) `; await client.query(trackQuery, [this.template_id, this.id, userSession, projectId]); await client.query('COMMIT'); if (updateResult.rows.length > 0) { Object.assign(this, updateResult.rows[0]); } return this; } catch (error) { await client.query('ROLLBACK'); throw error; } finally { client.release(); } } // Update rating async updateRating(newRating) { const query = ` UPDATE template_features SET user_rating = $2 WHERE id = $1 RETURNING * `; const result = await database.query(query, [this.id, newRating]); if (result.rows.length > 0) { Object.assign(this, result.rows[0]); } return this; } // Get feature by ID static async getById(id) { const query = ` SELECT tf.*, t.title as template_title, t.type as template_type FROM template_features tf JOIN templates t ON tf.template_id = t.id WHERE tf.id = $1 `; const result = await database.query(query, [id]); return result.rows.length > 0 ? new Feature(result.rows[0]) : null; } // Get features by type static async getByType(featureType, limit = 20) { const query = ` SELECT tf.*, t.title as template_title, t.type as template_type FROM template_features tf JOIN templates t ON tf.template_id = t.id WHERE tf.feature_type = $1 ORDER BY tf.usage_count DESC, tf.user_rating DESC LIMIT $2 `; const result = await database.query(query, [featureType, limit]); return result.rows.map(row => new Feature(row)); } // Get a template_features row by (template_id, feature_id) static async getByFeatureId(templateId, featureId) { const query = ` SELECT * FROM template_features WHERE template_id = $1 AND feature_id = $2 LIMIT 1 ` const result = await database.query(query, [templateId, featureId]) return result.rows.length > 0 ? new Feature(result.rows[0]) : null } // Get feature statistics static async getStats() { const query = ` SELECT feature_type, COUNT(*) as count, AVG(usage_count) as avg_usage, AVG(user_rating) as avg_rating FROM template_features GROUP BY feature_type ORDER BY count DESC `; const result = await database.query(query); return result.rows; } // Search features static async search(searchTerm, templateId = null) { let query = ` SELECT tf.*, t.title as template_title, t.type as template_type FROM template_features tf JOIN templates t ON tf.template_id = t.id WHERE (tf.name ILIKE $1 OR tf.description ILIKE $1) `; const params = [`%${searchTerm}%`]; if (templateId) { query += ` AND tf.template_id = $2`; params.push(templateId); } query += ` ORDER BY tf.usage_count DESC, tf.user_rating DESC`; const result = await database.query(query, params); return result.rows.map(row => new Feature(row)); } } module.exports = Feature;