training-academy/user-profile.html

759 lines
25 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>User Profile - webGame</title>
<style>
:root {
--primary-color: #8B5CF6;
--primary-dark: #7C3AED;
--primary-light: #A78BFA;
--secondary-color: #10B981;
--background: #0F172A;
--surface: #1E293B;
--surface-light: #334155;
--text-primary: #F1F5F9;
--text-secondary: #94A3B8;
--border: #475569;
--success: #10B981;
--warning: #F59E0B;
--error: #EF4444;
--gradient: linear-gradient(135deg, var(--primary-color), var(--primary-dark));
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
background: var(--background);
color: var(--text-primary);
line-height: 1.6;
padding: 20px;
min-height: 100vh;
}
.header {
text-align: center;
margin-bottom: 40px;
padding: 30px 20px;
background: var(--gradient);
border-radius: 20px;
box-shadow: 0 10px 30px rgba(139, 92, 246, 0.3);
}
.header h1 {
font-size: 2.5rem;
margin-bottom: 10px;
font-weight: 700;
}
.header p {
font-size: 1.1rem;
opacity: 0.9;
}
.profile-container {
max-width: 1400px;
margin: 0 auto;
display: grid;
grid-template-columns: 1fr 2fr;
gap: 30px;
margin-bottom: 30px;
}
.profile-info {
background: var(--surface);
border-radius: 15px;
padding: 30px;
border: 1px solid var(--border);
height: fit-content;
}
.profile-avatar {
width: 120px;
height: 120px;
background: var(--gradient);
border-radius: 50%;
margin: 0 auto 20px;
display: flex;
align-items: center;
justify-content: center;
font-size: 3rem;
font-weight: bold;
cursor: pointer;
transition: transform 0.3s ease;
}
.profile-avatar:hover {
transform: scale(1.05);
}
.profile-details h2 {
font-size: 1.8rem;
margin-bottom: 15px;
text-align: center;
}
.profile-field {
margin-bottom: 20px;
}
.profile-field label {
display: block;
font-weight: 600;
margin-bottom: 8px;
color: var(--text-secondary);
}
.profile-field input, .profile-field textarea {
width: 100%;
padding: 12px;
background: var(--background);
border: 1px solid var(--border);
border-radius: 8px;
color: var(--text-primary);
font-size: 1rem;
transition: border-color 0.3s ease;
}
.profile-field input:focus, .profile-field textarea:focus {
outline: none;
border-color: var(--primary-color);
}
.profile-field textarea {
min-height: 80px;
resize: vertical;
}
.stats-overview {
background: var(--surface);
border-radius: 15px;
padding: 30px;
border: 1px solid var(--border);
}
.stats-overview h3 {
font-size: 1.5rem;
margin-bottom: 20px;
display: flex;
align-items: center;
gap: 10px;
}
.quick-stats {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
gap: 15px;
margin-bottom: 20px;
}
.quick-stat {
background: var(--background);
border-radius: 10px;
padding: 20px;
text-align: center;
border: 1px solid var(--border);
}
.quick-stat-value {
font-size: 2rem;
font-weight: bold;
color: var(--primary-light);
display: block;
}
.quick-stat-label {
font-size: 0.9rem;
color: var(--text-secondary);
margin-top: 5px;
}
.level-progress {
background: var(--background);
border-radius: 10px;
padding: 20px;
margin-top: 20px;
}
.level-info {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 15px;
}
.level-bar {
background: var(--surface-light);
height: 20px;
border-radius: 10px;
overflow: hidden;
position: relative;
}
.level-fill {
background: var(--gradient);
height: 100%;
border-radius: 10px;
transition: width 0.3s ease;
}
.level-text {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
font-size: 0.8rem;
font-weight: bold;
color: white;
text-shadow: 1px 1px 2px rgba(0,0,0,0.7);
}
.achievements {
background: var(--surface);
border-radius: 15px;
padding: 30px;
border: 1px solid var(--border);
margin-top: 30px;
}
.achievements h3 {
font-size: 1.5rem;
margin-bottom: 20px;
display: flex;
align-items: center;
gap: 10px;
}
.achievement-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
gap: 15px;
}
.achievement {
background: var(--background);
border-radius: 10px;
padding: 20px;
text-align: center;
border: 1px solid var(--border);
transition: transform 0.3s ease;
}
.achievement.unlocked {
border-color: var(--success);
background: linear-gradient(135deg, rgba(16, 185, 129, 0.1), rgba(16, 185, 129, 0.05));
}
.achievement.locked {
opacity: 0.5;
}
.achievement:hover {
transform: translateY(-2px);
}
.achievement-icon {
font-size: 2.5rem;
margin-bottom: 10px;
display: block;
}
.achievement-title {
font-weight: bold;
margin-bottom: 5px;
}
.achievement-desc {
font-size: 0.9rem;
color: var(--text-secondary);
}
.action-buttons {
display: flex;
gap: 15px;
margin-top: 30px;
flex-wrap: wrap;
justify-content: center;
}
.btn {
padding: 12px 25px;
border: none;
border-radius: 8px;
font-size: 1rem;
cursor: pointer;
transition: all 0.3s ease;
text-decoration: none;
display: inline-flex;
align-items: center;
gap: 8px;
font-weight: 600;
}
.btn-primary {
background: var(--gradient);
color: white;
}
.btn-primary:hover {
transform: translateY(-2px);
box-shadow: 0 5px 15px rgba(139, 92, 246, 0.4);
}
.btn-secondary {
background: var(--surface-light);
color: var(--text-primary);
border: 1px solid var(--border);
}
.btn-secondary:hover {
background: var(--surface);
transform: translateY(-2px);
}
@media (max-width: 768px) {
.profile-container {
grid-template-columns: 1fr;
}
.header h1 {
font-size: 2rem;
}
.quick-stats {
grid-template-columns: repeat(2, 1fr);
}
.achievement-grid {
grid-template-columns: 1fr;
}
}
.export-section {
background: var(--surface);
border-radius: 15px;
padding: 30px;
border: 1px solid var(--border);
margin-top: 30px;
}
.export-section h3 {
font-size: 1.5rem;
margin-bottom: 20px;
display: flex;
align-items: center;
gap: 10px;
}
</style>
</head>
<body>
<div class="header">
<h1>👤 User Profile</h1>
<p>Customize your gaming experience and track your progress</p>
</div>
<div class="profile-container">
<div class="profile-info">
<div class="profile-avatar" id="profile-avatar">
<span id="avatar-text">U</span>
</div>
<div class="profile-details">
<h2>Profile Information</h2>
<div class="profile-field">
<label for="username">Username</label>
<input type="text" id="username" placeholder="Enter your username">
</div>
<div class="profile-field">
<label for="bio">Bio</label>
<textarea id="bio" placeholder="Tell us about yourself..."></textarea>
</div>
<div class="profile-field">
<label for="join-date">Member Since</label>
<input type="text" id="join-date" readonly>
</div>
<div class="profile-field">
<label for="theme">Preferred Theme</label>
<select id="theme" style="width: 100%; padding: 12px; background: var(--background); border: 1px solid var(--border); border-radius: 8px; color: var(--text-primary);">
<option value="dark">Dark Theme</option>
<option value="light">Light Theme</option>
<option value="auto">Auto (System)</option>
</select>
</div>
</div>
</div>
<div class="stats-overview">
<h3>📊 Statistics Overview</h3>
<div class="quick-stats">
<div class="quick-stat">
<span class="quick-stat-value" id="total-watch-time">0h 0m</span>
<div class="quick-stat-label">Total Watch Time</div>
</div>
<div class="quick-stat">
<span class="quick-stat-value" id="videos-watched">0</span>
<div class="quick-stat-label">Videos Watched</div>
</div>
<div class="quick-stat">
<span class="quick-stat-value" id="current-level">1</span>
<div class="quick-stat-label">Current Level</div>
</div>
<div class="quick-stat">
<span class="quick-stat-value" id="current-streak">0</span>
<div class="quick-stat-label">Day Streak</div>
</div>
</div>
<div class="level-progress">
<div class="level-info">
<span><strong>Level Progress</strong></span>
<span id="level-xp">0 / 100 XP</span>
</div>
<div class="level-bar">
<div class="level-fill" id="level-fill" style="width: 0%"></div>
<div class="level-text" id="level-text">0%</div>
</div>
</div>
</div>
</div>
<div class="achievements">
<h3>🏆 Achievements</h3>
<div class="achievement-grid" id="achievement-grid">
<!-- Achievements will be populated by JavaScript -->
</div>
</div>
<div class="export-section">
<h3>💾 Profile Data</h3>
<p>Manage your profile and statistics data</p>
<div class="action-buttons">
<button class="btn btn-secondary" onclick="viewDetailedStats()">📈 Detailed Statistics</button>
<button class="btn btn-secondary" onclick="exportProfile()">📤 Export Profile</button>
<button class="btn btn-secondary" onclick="importProfile()">📥 Import Profile</button>
<button class="btn btn-primary" onclick="saveProfile()">💾 Save Changes</button>
</div>
</div>
<div class="action-buttons">
<a href="index.html" class="btn btn-secondary">🏠 Back to Main Menu</a>
<a href="player-stats.html" class="btn btn-secondary">📊 Detailed Stats</a>
</div>
<script src="src/features/stats/playerStats.js"></script>
<script>
class UserProfile {
constructor() {
this.playerStats = window.PlayerStats;
this.profile = this.loadProfile();
this.achievements = this.initializeAchievements();
this.init();
}
loadProfile() {
const saved = localStorage.getItem('userProfile');
if (saved) {
return JSON.parse(saved);
}
// Default profile
return {
username: 'Player',
bio: '',
joinDate: new Date().toISOString().split('T')[0],
theme: 'dark',
avatar: 'P'
};
}
saveProfile() {
this.profile.username = document.getElementById('username').value || 'Player';
this.profile.bio = document.getElementById('bio').value;
this.profile.theme = document.getElementById('theme').value;
this.profile.avatar = this.profile.username.charAt(0).toUpperCase();
localStorage.setItem('userProfile', JSON.stringify(this.profile));
// Show success message
this.showNotification('Profile saved successfully!', 'success');
this.updateDisplay();
}
init() {
this.updateDisplay();
this.updateStats();
this.renderAchievements();
// Update stats every 30 seconds
setInterval(() => this.updateStats(), 30000);
}
updateDisplay() {
document.getElementById('username').value = this.profile.username;
document.getElementById('bio').value = this.profile.bio;
document.getElementById('join-date').value = this.formatDate(this.profile.joinDate);
document.getElementById('theme').value = this.profile.theme;
document.getElementById('avatar-text').textContent = this.profile.avatar;
}
updateStats() {
if (!this.playerStats) return;
const stats = this.playerStats.getStats();
// Update quick stats
document.getElementById('total-watch-time').textContent = this.formatTime(stats.totalWatchTime);
document.getElementById('videos-watched').textContent = stats.videosWatched;
document.getElementById('current-streak').textContent = stats.streaks.current;
// Calculate level based on XP
const level = this.calculateLevel(stats.totalWatchTime);
document.getElementById('current-level').textContent = level;
// Update level progress
this.updateLevelProgress(stats.totalWatchTime);
// Update achievements
this.updateAchievements(stats);
}
calculateLevel(watchTimeSeconds) {
// Level up every hour of watch time
return Math.floor(watchTimeSeconds / 3600) + 1;
}
updateLevelProgress(watchTimeSeconds) {
const currentLevel = this.calculateLevel(watchTimeSeconds);
const currentLevelStart = (currentLevel - 1) * 3600;
const nextLevelStart = currentLevel * 3600;
const progressInLevel = watchTimeSeconds - currentLevelStart;
const levelDuration = nextLevelStart - currentLevelStart;
const progressPercent = (progressInLevel / levelDuration) * 100;
document.getElementById('level-fill').style.width = progressPercent + '%';
document.getElementById('level-text').textContent = Math.round(progressPercent) + '%';
document.getElementById('level-xp').textContent = `${Math.round(progressInLevel)} / ${levelDuration} seconds`;
}
initializeAchievements() {
return [
{
id: 'first-video',
icon: '🎬',
title: 'First Steps',
description: 'Watch your first video',
condition: stats => stats.videosWatched >= 1
},
{
id: 'early-bird',
icon: '🌅',
title: 'Early Bird',
description: 'Watch 10 videos',
condition: stats => stats.videosWatched >= 10
},
{
id: 'marathon',
icon: '🏃',
title: 'Marathon Viewer',
description: 'Watch for 2+ hours in one session',
condition: stats => stats.sessions.longest >= 7200
},
{
id: 'curator',
icon: '📚',
title: 'Playlist Curator',
description: 'Create 5 playlists',
condition: stats => stats.playlistsCreated >= 5
},
{
id: 'consistent',
icon: '🔥',
title: 'Consistency King',
description: 'Maintain a 7-day streak',
condition: stats => stats.streaks.longest >= 7
},
{
id: 'completionist',
icon: '✅',
title: 'Completionist',
description: 'Complete 50 videos (90%+)',
condition: stats => stats.videosCompleted >= 50
},
{
id: 'night-owl',
icon: '🦉',
title: 'Night Owl',
description: 'Watch after midnight',
condition: stats => stats.sessions.total > 0 // Simplified for demo
},
{
id: 'collector',
icon: '💎',
title: 'Video Collector',
description: 'Watch 100 different videos',
condition: stats => stats.videosWatched >= 100
}
];
}
updateAchievements(stats) {
this.achievements.forEach(achievement => {
const element = document.getElementById(`achievement-${achievement.id}`);
if (element) {
if (achievement.condition(stats)) {
element.classList.add('unlocked');
element.classList.remove('locked');
} else {
element.classList.add('locked');
element.classList.remove('unlocked');
}
}
});
}
renderAchievements() {
const grid = document.getElementById('achievement-grid');
grid.innerHTML = '';
this.achievements.forEach(achievement => {
const element = document.createElement('div');
element.className = 'achievement locked';
element.id = `achievement-${achievement.id}`;
element.innerHTML = `
<span class="achievement-icon">${achievement.icon}</span>
<div class="achievement-title">${achievement.title}</div>
<div class="achievement-desc">${achievement.description}</div>
`;
grid.appendChild(element);
});
}
formatTime(seconds) {
const hours = Math.floor(seconds / 3600);
const minutes = Math.floor((seconds % 3600) / 60);
return `${hours}h ${minutes}m`;
}
formatDate(dateString) {
return new Date(dateString).toLocaleDateString();
}
showNotification(message, type = 'info') {
// Create notification element
const notification = document.createElement('div');
notification.style.cssText = `
position: fixed;
top: 20px;
right: 20px;
background: ${type === 'success' ? 'var(--success)' : 'var(--primary-color)'};
color: white;
padding: 15px 20px;
border-radius: 8px;
box-shadow: 0 5px 15px rgba(0,0,0,0.3);
z-index: 1000;
font-weight: 600;
transform: translateX(100%);
transition: transform 0.3s ease;
`;
notification.textContent = message;
document.body.appendChild(notification);
// Animate in
setTimeout(() => {
notification.style.transform = 'translateX(0)';
}, 100);
// Remove after 3 seconds
setTimeout(() => {
notification.style.transform = 'translateX(100%)';
setTimeout(() => {
if (notification.parentNode) {
notification.parentNode.removeChild(notification);
}
}, 300);
}, 3000);
}
}
// Global functions for buttons
function saveProfile() {
if (window.userProfile) {
window.userProfile.saveProfile();
}
}
function viewDetailedStats() {
window.location.href = 'player-stats.html';
}
function exportProfile() {
const profile = localStorage.getItem('userProfile');
const stats = localStorage.getItem('playerStats');
const exportData = {
profile: profile ? JSON.parse(profile) : null,
stats: stats ? JSON.parse(stats) : null,
exportDate: new Date().toISOString()
};
const blob = new Blob([JSON.stringify(exportData, null, 2)], { type: 'application/json' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = `webgame-profile-${new Date().toISOString().split('T')[0]}.json`;
a.click();
URL.revokeObjectURL(url);
}
function importProfile() {
const input = document.createElement('input');
input.type = 'file';
input.accept = '.json';
input.onchange = function(e) {
const file = e.target.files[0];
if (!file) return;
const reader = new FileReader();
reader.onload = function(e) {
try {
const data = JSON.parse(e.target.result);
if (data.profile) {
localStorage.setItem('userProfile', JSON.stringify(data.profile));
}
if (data.stats) {
localStorage.setItem('playerStats', JSON.stringify(data.stats));
}
// Reload page to apply changes
window.location.reload();
} catch (error) {
alert('Invalid profile file format');
}
};
reader.readAsText(file);
};
input.click();
}
// Initialize when page loads
document.addEventListener('DOMContentLoaded', () => {
window.userProfile = new UserProfile();
});
</script>
</body>
</html>