deckhearth/scripts/add-user-profile-fields.js

247 lines
8.1 KiB
JavaScript
Raw Normal View History

🎯 Build Comprehensive User Profile & Settings System 👤 Profile Page Features: - Complete user profile with avatar, name, username, bio, and email - Avatar upload with file validation (5MB limit, image types only) - Avatar generation functionality for custom avatars - Favorite games selection (MTG, Pokemon, Lorcana) - Collection statistics display (total cards, collections, decks, value) - Profile editing with real-time validation - Member since date and role display ⚙️ Settings Page Features: - Multi-section tabbed interface (Account, Security, Preferences, Notifications, Display) - Account settings: email (read-only), collection visibility, preferred currency - Security settings: password change with validation, 2FA toggle, account deletion - Preferences: cards per page (25/50/100), default view (grid/list) - Notifications: email notifications, marketing emails (toggle switches) - Display settings: theme (light/dark/system), language selection 🗄️ Database Schema Updates: - Added user profile fields: first_name, last_name, username, bio, avatar_url - Added preference fields: favorite_games (JSONB), collection_visibility, preferred_currency, cards_per_page, default_view - Added notification settings: notifications_email, notifications_marketing, two_factor_enabled - Added display settings: theme, language - Created user_settings table for complex settings - Created user_avatars table for avatar management - Added performance indexes and data validation constraints 📡 API Endpoints Created: - GET/PUT /api/user/profile - Profile information management - GET/PUT /api/user/settings - Settings and preferences management - PUT /api/user/password - Secure password change with bcrypt validation - GET /api/user/stats - Collection statistics and analytics 🔒 Security & Validation: - Password change requires current password verification - Username uniqueness validation - Input validation for all enum fields (currency, theme, view mode, etc.) - Proper error handling and user feedback - Authentication required for all user endpoints 🎨 UI/UX Features: - Beautiful fire-themed design matching app branding - Responsive design for mobile and desktop - Loading states and success/error messages - Avatar placeholder with user initials - Tabbed settings interface with icons - Toggle switches for boolean settings - Form validation with helpful error messages ✨ Additional Features: - Collection stats with game/rarity breakdowns - Recent activity tracking - Danger zone for account deletion with double confirmation - Member since display with formatted dates - Currency formatting for collection values - Game icons and themed styling throughout The profile and settings system is now fully functional with comprehensive user management! 👨‍💻✨
2025-07-26 19:05:55 -04:00
#!/usr/bin/env node
/**
* Add user profile fields to support enhanced profile and settings
*
* This script adds new columns to the users table for:
* - Profile information (first_name, last_name, username, bio, avatar_url)
* - Preferences (favorite_games, collection_visibility, preferred_currency, etc.)
* - Settings (notifications, display preferences)
*/
import dotenv from 'dotenv';
import { neon } from '@neondatabase/serverless';
// Load environment variables from .env.local
dotenv.config({ path: '.env.local' });
async function addUserProfileFields() {
const sql = neon(process.env.POSTGRES_URL);
try {
console.log('✅ Connecting to Neon database...');
// Add basic profile fields
console.log('👤 Adding basic profile fields...');
await sql`
ALTER TABLE users
ADD COLUMN IF NOT EXISTS first_name VARCHAR(255),
ADD COLUMN IF NOT EXISTS last_name VARCHAR(255),
ADD COLUMN IF NOT EXISTS username VARCHAR(255) UNIQUE,
ADD COLUMN IF NOT EXISTS bio TEXT,
ADD COLUMN IF NOT EXISTS avatar_url TEXT
`;
console.log('✅ Added basic profile fields');
// Add preference fields
console.log('⚙️ Adding preference fields...');
await sql`
ALTER TABLE users
ADD COLUMN IF NOT EXISTS favorite_games JSONB DEFAULT '["MTG"]',
ADD COLUMN IF NOT EXISTS collection_visibility VARCHAR(20) DEFAULT 'private',
ADD COLUMN IF NOT EXISTS preferred_currency VARCHAR(3) DEFAULT 'USD',
ADD COLUMN IF NOT EXISTS cards_per_page INTEGER DEFAULT 50,
ADD COLUMN IF NOT EXISTS default_view VARCHAR(10) DEFAULT 'grid'
`;
console.log('✅ Added preference fields');
// Add notification settings
console.log('🔔 Adding notification settings...');
await sql`
ALTER TABLE users
ADD COLUMN IF NOT EXISTS notifications_email BOOLEAN DEFAULT true,
ADD COLUMN IF NOT EXISTS notifications_marketing BOOLEAN DEFAULT false,
ADD COLUMN IF NOT EXISTS two_factor_enabled BOOLEAN DEFAULT false
`;
console.log('✅ Added notification settings');
// Add display settings
console.log('🎨 Adding display settings...');
await sql`
ALTER TABLE users
ADD COLUMN IF NOT EXISTS theme VARCHAR(10) DEFAULT 'system',
ADD COLUMN IF NOT EXISTS language VARCHAR(5) DEFAULT 'en'
`;
console.log('✅ Added display settings');
// Create user_settings table for more complex settings
console.log('📊 Creating user_settings table...');
await sql`
CREATE TABLE IF NOT EXISTS user_settings (
id SERIAL PRIMARY KEY,
user_id INTEGER REFERENCES users(id) ON DELETE CASCADE,
setting_key VARCHAR(100) NOT NULL,
setting_value JSONB NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
UNIQUE(user_id, setting_key)
)
`;
console.log('✅ Created user_settings table');
// Create user_avatars table for avatar management
console.log('🖼️ Creating user_avatars table...');
await sql`
CREATE TABLE IF NOT EXISTS user_avatars (
id SERIAL PRIMARY KEY,
user_id INTEGER REFERENCES users(id) ON DELETE CASCADE,
filename VARCHAR(255) NOT NULL,
original_name VARCHAR(255),
mime_type VARCHAR(100),
file_size INTEGER,
file_path TEXT NOT NULL,
is_active BOOLEAN DEFAULT true,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
`;
console.log('✅ Created user_avatars table');
// Add indexes for performance
console.log('🚀 Adding indexes for performance...');
await sql`CREATE INDEX IF NOT EXISTS idx_users_username ON users(username)`;
await sql`CREATE INDEX IF NOT EXISTS idx_users_email ON users(email)`;
await sql`CREATE INDEX IF NOT EXISTS idx_user_settings_user_id ON user_settings(user_id)`;
await sql`CREATE INDEX IF NOT EXISTS idx_user_settings_key ON user_settings(setting_key)`;
await sql`CREATE INDEX IF NOT EXISTS idx_user_avatars_user_id ON user_avatars(user_id)`;
await sql`CREATE INDEX IF NOT EXISTS idx_user_avatars_active ON user_avatars(user_id, is_active)`;
console.log('✅ Added performance indexes');
// Add constraints and validation (skip if they already exist)
console.log('🔒 Adding constraints and validation...');
try {
await sql`
ALTER TABLE users
ADD CONSTRAINT check_collection_visibility
CHECK (collection_visibility IN ('private', 'public', 'unlisted'))
`;
} catch (error) {
if (!error.message.includes('already exists')) {
throw error;
}
}
try {
await sql`
ALTER TABLE users
ADD CONSTRAINT check_preferred_currency
CHECK (preferred_currency IN ('USD', 'EUR', 'GBP', 'CAD', 'JPY'))
`;
} catch (error) {
if (!error.message.includes('already exists')) {
throw error;
}
}
try {
await sql`
ALTER TABLE users
ADD CONSTRAINT check_cards_per_page
CHECK (cards_per_page IN (25, 50, 100))
`;
} catch (error) {
if (!error.message.includes('already exists')) {
throw error;
}
}
try {
await sql`
ALTER TABLE users
ADD CONSTRAINT check_default_view
CHECK (default_view IN ('grid', 'list'))
`;
} catch (error) {
if (!error.message.includes('already exists')) {
throw error;
}
}
try {
await sql`
ALTER TABLE users
ADD CONSTRAINT check_theme
CHECK (theme IN ('light', 'dark', 'system'))
`;
} catch (error) {
if (!error.message.includes('already exists')) {
throw error;
}
}
try {
await sql`
ALTER TABLE users
ADD CONSTRAINT check_language
CHECK (language IN ('en', 'es', 'fr', 'de', 'ja'))
`;
} catch (error) {
if (!error.message.includes('already exists')) {
throw error;
}
}
console.log('✅ Added constraints and validation');
// Update existing users with default values
console.log('🔄 Updating existing users with default values...');
await sql`
UPDATE users
SET
favorite_games = '["MTG"]'::jsonb,
collection_visibility = 'private',
preferred_currency = 'USD',
cards_per_page = 50,
default_view = 'grid',
notifications_email = true,
notifications_marketing = false,
two_factor_enabled = false,
theme = 'system',
language = 'en'
WHERE
favorite_games IS NULL OR
collection_visibility IS NULL OR
preferred_currency IS NULL OR
cards_per_page IS NULL OR
default_view IS NULL OR
notifications_email IS NULL OR
notifications_marketing IS NULL OR
two_factor_enabled IS NULL OR
theme IS NULL OR
language IS NULL
`;
console.log('✅ Updated existing users with defaults');
console.log('\n🎉 User profile fields added successfully!');
console.log('\n📋 Summary of changes:');
console.log(' 📝 Basic Profile: first_name, last_name, username, bio, avatar_url');
console.log(' ⚙️ Preferences: favorite_games, collection_visibility, preferred_currency, cards_per_page, default_view');
console.log(' 🔔 Notifications: notifications_email, notifications_marketing, two_factor_enabled');
console.log(' 🎨 Display: theme, language');
console.log(' 📊 New Tables: user_settings, user_avatars');
console.log(' 🚀 Performance: Added indexes for fast queries');
console.log(' 🔒 Validation: Added constraints for data integrity');
} catch (error) {
console.error('❌ Failed to add user profile fields:', error.message);
console.error('Full error:', error);
process.exit(1);
}
}
// Run the migration if this script is executed directly
if (import.meta.url === `file://${process.argv[1]}`) {
addUserProfileFields();
}
export { addUserProfileFields };