![]() Server : Apache/2 System : Linux server-15-235-50-60 5.15.0-164-generic #174-Ubuntu SMP Fri Nov 14 20:25:16 UTC 2025 x86_64 User : gositeme ( 1004) PHP Version : 8.2.29 Disable Function : exec,system,passthru,shell_exec,proc_close,proc_open,dl,popen,show_source,posix_kill,posix_mkfifo,posix_getpwuid,posix_setpgid,posix_setsid,posix_setuid,posix_setgid,posix_seteuid,posix_setegid,posix_uname Directory : /home/gositeme/domains/soundstudiopro.com/public_html/ |
<?php
session_start();
// Check if user is logged in
if (!isset($_SESSION['user_id'])) {
header('Location: /auth/login.php');
exit;
}
require_once 'config/database.php';
require_once 'includes/site_settings_helper.php';
require_once 'includes/translations.php';
$pdo = getDBConnection();
// Get current tab
$current_tab = $_GET['tab'] ?? 'purchases';
// Force fresh data fetch - no caching
// Get user data - always fetch fresh from database
// Explicitly select all needed columns to ensure they're retrieved
$stmt = $pdo->prepare("
SELECT
u.id,
u.name,
u.email,
u.credits,
COALESCE(u.plan, 'free') as plan,
u.created_at,
u.stripe_customer_id,
COALESCE(up.custom_url, '') as custom_url
FROM users u
LEFT JOIN user_profiles up ON u.id = up.user_id
WHERE u.id = ?
");
$stmt->execute([$_SESSION['user_id']]);
$user = $stmt->fetch(PDO::FETCH_ASSOC);
// Ensure user data exists
if (!$user || !is_array($user)) {
error_log("ERROR: Failed to fetch user data for user ID: " . $_SESSION['user_id']);
header('Location: /auth/login.php');
exit;
}
// Ensure required fields exist in the user array
if (!isset($user['email']) || !isset($user['name'])) {
error_log("ERROR: User data missing required fields. User ID: " . $_SESSION['user_id']);
error_log("User data keys: " . implode(', ', array_keys($user)));
// Try to fetch again with a simpler query as fallback
$fallback_stmt = $pdo->prepare("SELECT id, name, email, credits, plan, created_at, stripe_customer_id FROM users WHERE id = ?");
$fallback_stmt->execute([$_SESSION['user_id']]);
$fallback_user = $fallback_stmt->fetch(PDO::FETCH_ASSOC);
if ($fallback_user) {
// Merge with existing user data
$user = array_merge($user, $fallback_user);
// Get custom_url separately
$url_stmt = $pdo->prepare("SELECT custom_url FROM user_profiles WHERE user_id = ?");
$url_stmt->execute([$_SESSION['user_id']]);
$url_data = $url_stmt->fetch(PDO::FETCH_ASSOC);
if ($url_data) {
$user['custom_url'] = $url_data['custom_url'] ?? '';
} else {
$user['custom_url'] = '';
}
}
}
// If custom_url is not set or is empty, try fetching it directly from user_profiles
if (!isset($user['custom_url']) || (isset($user['custom_url']) && ($user['custom_url'] === '' || $user['custom_url'] === null))) {
try {
$url_stmt = $pdo->prepare("SELECT custom_url FROM user_profiles WHERE user_id = ?");
$url_stmt->execute([$_SESSION['user_id']]);
$url_data = $url_stmt->fetch(PDO::FETCH_ASSOC);
if ($url_data && isset($url_data['custom_url']) && !empty(trim($url_data['custom_url']))) {
$user['custom_url'] = trim($url_data['custom_url']);
error_log("Custom URL fetched directly from user_profiles: " . $user['custom_url']);
} else {
$user['custom_url'] = '';
error_log("Custom URL not found in user_profiles for user ID: " . $_SESSION['user_id']);
}
} catch (Exception $e) {
error_log("Error fetching custom_url: " . $e->getMessage());
$user['custom_url'] = '';
}
}
// Debug: Log user data to verify it's being loaded
error_log("Account Settings - User ID: " . $_SESSION['user_id']);
error_log("Account Settings - User Name (DB): " . (isset($user['name']) ? var_export($user['name'], true) : 'NOT SET'));
error_log("Account Settings - User Email (DB): " . (isset($user['email']) ? var_export($user['email'], true) : 'NOT SET'));
error_log("Account Settings - Custom URL (DB): " . (isset($user['custom_url']) ? var_export($user['custom_url'], true) : 'NOT SET'));
if (isset($user) && is_array($user)) {
error_log("Account Settings - Full user array keys: " . implode(', ', array_keys($user)));
}
// Get purchase count separately to avoid GROUP BY issues
// This ensures accurate count regardless of which tab is active
// IMPORTANT: This must run BEFORE any tab-specific queries to ensure count is always available
try {
$purchase_count_stmt = $pdo->prepare("
SELECT COUNT(*) as total_purchases, COALESCE(SUM(credits_used), 0) as total_credits_spent
FROM track_purchases
WHERE user_id = ?
");
$purchase_count_stmt->execute([$_SESSION['user_id']]);
$purchase_stats = $purchase_count_stmt->fetch(PDO::FETCH_ASSOC);
// COUNT(*) always returns a number, so we can safely cast it
$user['total_purchases'] = intval($purchase_stats['total_purchases']);
$user['total_credits_spent'] = intval($purchase_stats['total_credits_spent'] ?? 0);
// Log for debugging (can be removed later)
if ($user['total_purchases'] > 0) {
error_log("Purchase count for user " . $_SESSION['user_id'] . ": " . $user['total_purchases']);
}
} catch (Exception $e) {
error_log("Error getting purchase count: " . $e->getMessage());
$user['total_purchases'] = 0;
$user['total_credits_spent'] = 0;
}
// Get total ticket count for overview
try {
$ticket_count_stmt = $pdo->prepare("
SELECT COUNT(*) as total_tickets
FROM event_tickets
WHERE user_id = ?
");
$ticket_count_stmt->execute([$_SESSION['user_id']]);
$ticket_stats = $ticket_count_stmt->fetch(PDO::FETCH_ASSOC);
$user['total_tickets'] = intval($ticket_stats['total_tickets'] ?? 0);
} catch (Exception $e) {
error_log("Error getting ticket count: " . $e->getMessage());
$user['total_tickets'] = 0;
}
// Check for upgrade required message
$upgrade_required = isset($_GET['upgrade']) && $_GET['upgrade'] === 'required';
$upgrade_message = '';
if ($upgrade_required) {
$upgrade_message = 'Access to this feature requires a Starter plan or higher. Please upgrade your subscription to continue.';
}
function formatEventDateRange($startDate, $endDate = null) {
if (empty($startDate)) {
return '';
}
try {
$start = new DateTime($startDate);
if (!empty($endDate)) {
$end = new DateTime($endDate);
if ($start->format('Y-m-d') === $end->format('Y-m-d')) {
return $start->format('M j, Y g:i A') . ' - ' . $end->format('g:i A');
}
return $start->format('M j, Y g:i A') . ' → ' . $end->format('M j, Y g:i A');
}
return $start->format('M j, Y g:i A');
} catch (Exception $e) {
return $startDate;
}
}
function buildCalendarLink($title, $startDate, $endDate, $locationLabel) {
if (empty($startDate)) {
return '#';
}
try {
$start = new DateTime($startDate);
$end = $endDate ? new DateTime($endDate) : (clone $start)->modify('+2 hours');
$startStr = $start->format('Ymd\THis\Z');
$endStr = $end->format('Ymd\THis\Z');
$text = urlencode($title ?? 'SoundStudioPro Event');
$details = urlencode('Ticket from SoundStudioPro');
$location = urlencode($locationLabel ?? '');
return "https://www.google.com/calendar/render?action=TEMPLATE&text={$text}&dates={$startStr}/{$endStr}&details={$details}&location={$location}";
} catch (Exception $e) {
return '#';
}
}
// If we just added a card and the user doesn't have a customer ID, refresh from database
if ($current_tab === 'payment' && empty($user['stripe_customer_id'])) {
error_log("User has no customer ID, checking if one was created recently...");
// Check if user has a customer ID now (might have been created during card addition)
$stmt = $pdo->prepare("SELECT stripe_customer_id FROM users WHERE id = ?");
$stmt->execute([$_SESSION['user_id']]);
$updated_user = $stmt->fetch();
if (!empty($updated_user['stripe_customer_id'])) {
$user['stripe_customer_id'] = $updated_user['stripe_customer_id'];
error_log("Found updated customer ID: " . $updated_user['stripe_customer_id']);
}
}
// Fetch payment methods, credit purchases, track purchases, and credit usage for payment tab
$payment_methods = [];
$credit_purchases = [];
$track_purchases = [];
$credit_usage = [];
if ($current_tab === 'payment') {
try {
// Fetch credit purchases
$stmt = $pdo->prepare("SELECT * FROM credit_purchases WHERE user_id = ? ORDER BY created_at DESC LIMIT 50");
$stmt->execute([$_SESSION['user_id']]);
$credit_purchases = $stmt->fetchAll();
// Fetch track purchases
$stmt = $pdo->prepare("SELECT tp.*, mt.title FROM track_purchases tp JOIN music_tracks mt ON tp.track_id = mt.id WHERE tp.user_id = ? ORDER BY tp.purchased_at DESC LIMIT 50");
$stmt->execute([$_SESSION['user_id']]);
$track_purchases = $stmt->fetchAll();
// Fetch credit usage
$stmt = $pdo->prepare("SELECT cu.*, mt.title FROM credit_usage cu LEFT JOIN music_tracks mt ON cu.track_id = mt.id WHERE cu.user_id = ? ORDER BY cu.created_at DESC LIMIT 50");
$stmt->execute([$_SESSION['user_id']]);
$credit_usage = $stmt->fetchAll();
// Fetch payment methods from Stripe
$stripe_secret = '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';
$customer_id = $user['stripe_customer_id'];
// Create Stripe customer if doesn't exist
if (!$customer_id) {
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "https://api.stripe.com/v1/customers");
curl_setopt($ch, CURLOPT_USERPWD, $stripe_secret . ":");
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query([
'email' => $user['email'],
'name' => $user['name']
]));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$response = curl_exec($ch);
$customer = json_decode($response, true);
curl_close($ch);
if (!empty($customer['id'])) {
// Save the new customer ID to database
$stmt = $pdo->prepare("UPDATE users SET stripe_customer_id = ? WHERE id = ?");
$stmt->execute([$customer['id'], $user['id']]);
$customer_id = $customer['id'];
$user['stripe_customer_id'] = $customer['id'];
}
}
// Fetch payment methods if customer exists
if ($customer_id) {
error_log("Fetching payment methods for customer: $customer_id");
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "https://api.stripe.com/v1/payment_methods?customer=$customer_id&type=card");
curl_setopt($ch, CURLOPT_USERPWD, $stripe_secret . ":");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$response = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
error_log("Payment methods API response code: $http_code");
error_log("Payment methods API response: " . substr($response, 0, 500));
$payment_data = json_decode($response, true);
$payment_methods = $payment_data['data'] ?? [];
error_log("Found " . count($payment_methods) . " payment methods");
foreach ($payment_methods as $pm) {
error_log("Payment method: " . $pm['id'] . " - " . $pm['card']['brand'] . " ending in " . $pm['card']['last4']);
}
} else {
error_log("No customer ID found for user: " . $_SESSION['user_id']);
// Let's also check what's in the database
$stmt = $pdo->prepare("SELECT stripe_customer_id FROM users WHERE id = ?");
$stmt->execute([$_SESSION['user_id']]);
$db_user = $stmt->fetch();
error_log("Database stripe_customer_id for user " . $_SESSION['user_id'] . ": " . ($db_user['stripe_customer_id'] ?? 'NULL'));
}
} catch (Exception $e) {
// Log error but don't break the page
error_log("Payment tab error: " . $e->getMessage());
$credit_purchases = [];
$track_purchases = [];
$credit_usage = [];
$payment_methods = [];
}
}
// Get user's purchases if on purchases tab
$purchases = [];
if ($current_tab === 'purchases') {
$stmt = $pdo->prepare("
SELECT
tp.id as purchase_id,
tp.price_paid,
tp.credits_used,
tp.purchase_date,
tp.download_count,
tp.last_downloaded,
mt.id as track_id,
mt.title,
mt.audio_url,
mt.duration,
mt.music_type,
mt.metadata,
mt.prompt,
u.name as artist_name,
u.id as artist_id
FROM track_purchases tp
JOIN music_tracks mt ON tp.track_id = mt.id
JOIN users u ON mt.user_id = u.id
WHERE tp.user_id = ?
ORDER BY tp.purchase_date DESC
");
$stmt->execute([$_SESSION['user_id']]);
$purchases = $stmt->fetchAll();
// ALWAYS update total_purchases from actual purchases array
// This ensures the count matches what's actually displayed and is most reliable
$user['total_purchases'] = count($purchases);
// Fetch variations for each purchased track
foreach ($purchases as &$purchase) {
$purchase['variations'] = [];
// Check if audio_variations table exists
try {
$checkTable = $pdo->query("SHOW TABLES LIKE 'audio_variations'");
if ($checkTable->rowCount() > 0) {
$varStmt = $pdo->prepare("
SELECT
id,
variation_index,
audio_url,
duration,
title,
tags
FROM audio_variations
WHERE track_id = ?
ORDER BY variation_index ASC
");
$varStmt->execute([$purchase['track_id']]);
$purchase['variations'] = $varStmt->fetchAll();
}
} catch (Exception $e) {
error_log("Error fetching variations for track {$purchase['track_id']}: " . $e->getMessage());
$purchase['variations'] = [];
}
}
unset($purchase); // Break reference
}
// Get user's tickets if on tickets tab
$tickets_list = [];
if ($current_tab === 'tickets') {
$stmt = $pdo->prepare("
SELECT
et.*,
e.title,
e.start_date,
e.end_date,
e.location,
e.venue_name,
e.address,
e.cover_image,
e.banner_image,
e.event_type,
e.id AS event_id,
u.name AS organizer_name
FROM event_tickets et
JOIN events e ON et.event_id = e.id
JOIN users u ON e.creator_id = u.id
WHERE et.user_id = ?
ORDER BY e.start_date DESC, et.purchase_date DESC
");
$stmt->execute([$_SESSION['user_id']]);
$tickets_list = $stmt->fetchAll(PDO::FETCH_ASSOC);
}
// Get credit transaction history
$transactions = [];
$subscription_info = null;
$subscription_usage = null;
if ($current_tab === 'credits') {
$stmt = $pdo->prepare("
SELECT
ct.*
FROM credit_transactions ct
WHERE ct.user_id = ?
ORDER BY ct.created_at DESC
LIMIT 50
");
$stmt->execute([$_SESSION['user_id']]);
$transactions = $stmt->fetchAll();
// Translate transaction types
foreach ($transactions as &$transaction) {
switch ($transaction['type']) {
case 'purchase':
$transaction['type_display'] = t('account.credit_purchase');
break;
case 'usage':
$transaction['type_display'] = t('account.track_purchase');
break;
case 'refund':
$transaction['type_display'] = t('account.refund');
break;
case 'bonus':
$transaction['type_display'] = t('account.bonus_credits');
break;
default:
$transaction['type_display'] = $transaction['type'];
}
}
unset($transaction);
// Get subscription information for credits tab
try {
require_once __DIR__ . '/utils/subscription_helpers.php';
$subscription_info = getSubscriptionInfo($_SESSION['user_id']);
if ($subscription_info) {
$plans_config = require __DIR__ . '/config/subscription_plans.php';
try {
// FIX: Use getEffectivePlan() to get correct plan for usage tracking
require_once __DIR__ . '/utils/subscription_helpers.php';
$effective_plan_credits_usage = getEffectivePlan($_SESSION['user_id']);
$subscription_usage = getMonthlyTrackUsage($_SESSION['user_id'], $effective_plan_credits_usage);
} catch (Exception $e) {
error_log("Error getting subscription usage in credits tab: " . $e->getMessage());
$subscription_usage = null;
}
}
} catch (Exception $e) {
error_log("Error getting subscription info in credits tab: " . $e->getMessage());
$subscription_info = null;
}
}
// Set page variables for header
$page_title = t('account.page_title');
$page_description = t('account.page_description');
$current_page = 'account';
include 'includes/header.php';
?>
<main>
<style>
.account-container {
max-width: 1200px;
margin: 0 auto;
padding: 2rem;
box-sizing: border-box;
width: 100%;
}
.account-header {
text-align: center;
margin-bottom: 3rem;
}
.account-title {
font-size: 3rem;
font-weight: 700;
background: linear-gradient(135deg, #667eea, #764ba2);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
margin-bottom: 1rem;
}
.account-subtitle {
font-size: 1.4rem;
color: #a0aec0;
}
.account-tabs {
display: flex;
justify-content: center;
margin-bottom: 3rem;
background: rgba(255, 255, 255, 0.05);
border-radius: 12px;
padding: 0.5rem;
gap: 0.5rem;
flex-wrap: wrap;
}
.account-tab {
padding: 1rem 2rem;
border-radius: 8px;
text-decoration: none;
color: #a0aec0;
font-weight: 600;
transition: all 0.3s ease;
display: flex;
align-items: center;
gap: 0.5rem;
white-space: nowrap;
}
.account-tab:hover {
color: white;
background: rgba(255, 255, 255, 0.1);
}
.account-tab.active {
background: linear-gradient(135deg, #667eea, #764ba2);
color: white;
}
.tab-content {
background: rgba(255, 255, 255, 0.05);
border-radius: 16px;
padding: 2rem;
border: 1px solid rgba(255, 255, 255, 0.1);
}
.tab-content h3 {
color: white;
font-size: 2rem;
margin-bottom: 1.5rem;
display: flex;
align-items: center;
gap: 0.5rem;
}
/* Account Overview Styles */
.account-overview {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 2rem;
margin-bottom: 3rem;
}
.overview-card {
background: rgba(255, 255, 255, 0.05);
border: 1px solid rgba(255, 255, 255, 0.1);
border-radius: 16px;
padding: 2rem;
text-align: center;
transition: all 0.3s ease;
}
.overview-card:hover {
transform: translateY(-5px);
border-color: rgba(102, 126, 234, 0.3);
}
.overview-icon {
font-size: 3rem;
margin-bottom: 1rem;
background: linear-gradient(135deg, #667eea, #764ba2);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
.overview-number {
font-size: 2.4rem;
font-weight: 700;
color: white;
margin-bottom: 0.5rem;
}
.overview-label {
font-size: 1.2rem;
color: #a0aec0;
}
.tab-lead {
color: #a0aec0;
font-size: 1.3rem;
margin-bottom: 2rem;
}
/* Purchases Tab Styles */
.purchases-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(350px, 1fr));
gap: 2rem;
}
.purchase-card {
background: rgba(255, 255, 255, 0.05);
border: 1px solid rgba(255, 255, 255, 0.1);
border-radius: 16px;
padding: 2rem;
transition: all 0.3s ease;
}
.purchase-card:hover {
transform: translateY(-5px);
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3);
border-color: rgba(102, 126, 234, 0.3);
}
.purchase-header {
display: flex;
justify-content: space-between;
align-items: flex-start;
margin-bottom: 1.5rem;
}
.purchase-title {
font-size: 1.8rem;
font-weight: 600;
color: white;
margin-bottom: 0.5rem;
}
.purchase-title-link {
color: white;
text-decoration: none;
transition: all 0.3s ease;
}
.purchase-title-link:hover {
color: #667eea;
text-decoration: underline;
}
.purchase-artist {
font-size: 1.2rem;
color: #667eea;
}
.purchase-artist-link {
color: #667eea;
text-decoration: none;
transition: all 0.3s ease;
}
.purchase-artist-link:hover {
color: #764ba2;
text-decoration: underline;
}
.purchase-price {
background: linear-gradient(135deg, #48bb78, #38a169);
color: white;
padding: 0.5rem 1rem;
border-radius: 8px;
font-size: 1.2rem;
font-weight: 600;
}
.purchase-info {
display: flex;
gap: 1rem;
margin-bottom: 1.5rem;
font-size: 1.2rem;
color: #a0aec0;
}
.purchase-date {
color: #a0aec0;
font-size: 1.1rem;
margin-bottom: 1.5rem;
}
.purchase-actions {
display: flex;
flex-direction: column;
gap: 1rem;
}
.purchase-btn {
flex: 1;
padding: 1rem;
border: none;
border-radius: 8px;
font-size: 1.4rem;
font-weight: 600;
cursor: pointer;
transition: all 0.3s ease;
text-decoration: none;
text-align: center;
display: inline-flex;
align-items: center;
justify-content: center;
gap: 0.5rem;
}
/* Tickets Tab */
.tickets-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
gap: 2rem;
}
.ticket-card {
background: rgba(12, 16, 34, 0.75);
border: 1px solid rgba(103, 126, 234, 0.2);
border-radius: 18px;
padding: 2rem;
position: relative;
overflow: hidden;
backdrop-filter: blur(12px);
transition: transform 0.3s ease, box-shadow 0.3s ease;
}
.ticket-card:hover {
transform: translateY(-6px);
box-shadow: 0 20px 45px rgba(79, 172, 254, 0.2);
border-color: rgba(103, 126, 234, 0.45);
}
.ticket-header {
display: flex;
justify-content: space-between;
align-items: flex-start;
gap: 1rem;
margin-bottom: 1.5rem;
}
.ticket-title {
font-size: 1.8rem;
color: #fff;
margin-bottom: 0.5rem;
}
.ticket-organizer {
color: #a0aec0;
font-size: 1.2rem;
}
.ticket-status {
padding: 0.4rem 1rem;
border-radius: 999px;
font-size: 1.1rem;
font-weight: 600;
text-transform: capitalize;
}
.ticket-status.confirmed {
background: rgba(72, 187, 120, 0.2);
color: #48bb78;
}
.ticket-status.pending {
background: rgba(237, 137, 54, 0.2);
color: #ed8936;
}
.ticket-status.used {
background: rgba(66, 153, 225, 0.2);
color: #4299e1;
}
.ticket-status.cancelled {
background: rgba(245, 101, 101, 0.2);
color: #f56565;
}
.ticket-info {
display: flex;
flex-direction: column;
gap: 0.75rem;
margin-bottom: 1.5rem;
font-size: 1.3rem;
color: #cbd5f5;
}
.ticket-info span i {
margin-right: 0.6rem;
color: #667eea;
}
.ticket-meta {
display: flex;
flex-wrap: wrap;
gap: 1rem;
margin-bottom: 1.5rem;
font-size: 1.2rem;
color: #a0aec0;
}
.ticket-actions {
display: flex;
flex-wrap: wrap;
gap: 0.75rem;
}
.ticket-btn {
flex: 1;
min-width: 140px;
text-align: center;
border-radius: 10px;
padding: 0.9rem 1.2rem;
font-weight: 600;
font-size: 1.2rem;
border: 1px solid rgba(255, 255, 255, 0.2);
color: #e2e8f0;
text-decoration: none;
transition: all 0.3s ease;
background: transparent;
cursor: pointer;
}
.ticket-btn.primary {
background: linear-gradient(135deg, #667eea, #764ba2);
border: none;
color: #fff;
}
.ticket-btn:hover {
transform: translateY(-2px);
box-shadow: 0 10px 25px rgba(102, 126, 234, 0.35);
}
.tickets-empty {
text-align: center;
padding: 4rem 2rem;
color: #a0aec0;
}
.tickets-empty i {
font-size: 4rem;
margin-bottom: 1rem;
color: #667eea;
}
.tickets-empty h3 {
color: #fff;
font-size: 2rem;
margin-bottom: 0.5rem;
}
.tickets-empty p {
max-width: 480px;
margin: 0 auto 1.5rem;
}
.tickets-empty .browse-btn {
display: inline-flex;
align-items: center;
gap: 0.5rem;
}
.download-options {
display: flex;
flex-direction: column;
gap: 0.5rem;
}
.download-btn {
background: linear-gradient(135deg, #667eea, #764ba2);
color: white;
}
.download-btn:hover {
transform: translateY(-2px);
box-shadow: 0 8px 25px rgba(102, 126, 234, 0.4);
}
.variations-downloads {
display: flex;
flex-direction: column;
gap: 0.5rem;
margin-top: 0.5rem;
padding-top: 0.5rem;
border-top: 1px solid rgba(255, 255, 255, 0.1);
}
.variations-label {
font-size: 1.2rem;
color: #a0aec0;
font-weight: 600;
margin-bottom: 0.3rem;
}
.play-btn {
background: rgba(255, 255, 255, 0.1);
color: #a0aec0;
border: 1px solid rgba(255, 255, 255, 0.2);
}
.play-btn:hover {
background: rgba(255, 255, 255, 0.2);
color: white;
}
/* Mobile-specific styles */
@media (max-width: 768px) {
.account-container {
padding: 1rem;
}
.account-header {
margin-bottom: 2rem;
}
.account-title {
font-size: 2rem;
}
.account-subtitle {
font-size: 1.2rem;
}
.account-tabs {
flex-direction: column;
gap: 0.5rem;
padding: 1rem;
}
.account-tab {
width: 100%;
padding: 1rem;
justify-content: center;
font-size: 1.3rem;
}
.tab-content {
padding: 1.5rem;
}
.account-overview {
grid-template-columns: 1fr;
gap: 1rem;
}
.overview-card {
padding: 1.5rem;
}
.overview-icon {
font-size: 2.5rem;
}
.overview-number {
font-size: 2rem;
}
.overview-label {
font-size: 1.2rem;
}
.purchases-grid {
grid-template-columns: 1fr;
gap: 1.5rem;
}
.purchase-card {
padding: 1.5rem;
}
.purchase-title {
font-size: 1.5rem;
}
.purchase-actions {
flex-direction: column;
gap: 0.8rem;
}
/* Override mobile.css circular play button for purchase buttons */
.purchase-actions .purchase-btn.play-btn {
width: 100% !important;
height: auto !important;
min-height: 50px !important;
border-radius: 12px !important;
padding: 1rem 1.5rem !important;
font-size: 1.2rem !important;
display: flex !important;
flex-direction: row !important;
align-items: center !important;
justify-content: center !important;
gap: 0.8rem !important;
background: rgba(255, 255, 255, 0.1) !important;
color: #ffffff !important;
border: 2px solid rgba(255, 255, 255, 0.2) !important;
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2) !important;
}
.purchase-actions .purchase-btn.play-btn:hover {
background: rgba(255, 255, 255, 0.2) !important;
transform: translateY(-2px) !important;
box-shadow: 0 6px 20px rgba(0, 0, 0, 0.3) !important;
}
.purchase-actions .purchase-btn.play-btn i {
font-size: 1.2rem !important;
}
.purchase-actions .purchase-btn.play-btn span {
display: inline !important;
}
.purchase-actions .purchase-btn.download-btn {
width: 100% !important;
min-height: 50px !important;
padding: 1rem 1.5rem !important;
font-size: 1.2rem !important;
}
.credits-overview {
padding: 1.5rem;
}
.credits-balance {
font-size: 3rem;
color: white !important;
}
.buy-credits-btn {
width: 100%;
padding: 1.2rem;
font-size: 1.3rem;
}
.transactions-list {
gap: 1rem;
}
.transaction-item {
padding: 1.2rem;
flex-direction: column;
gap: 1rem;
}
.transaction-amount {
text-align: left;
font-size: 1.4rem;
}
.settings-section {
margin-bottom: 2rem;
}
.settings-section h3 {
font-size: 1.6rem;
}
.settings-section p {
font-size: 1.2rem;
}
.form-label {
font-size: 1.3rem;
}
.form-input {
font-size: 16px !important; /* Prevents iOS zoom */
padding: 1rem;
min-height: 48px;
}
.url-input-group {
flex-direction: column;
}
.url-prefix {
padding: 0.8rem;
border-right: none;
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
}
.url-input {
padding: 1rem;
}
.save-btn {
width: 100%;
padding: 1.2rem;
font-size: 1.4rem;
}
.empty-state {
padding: 2rem 1rem;
}
.empty-state h3 {
font-size: 1.6rem;
}
.empty-state p {
font-size: 1.2rem;
}
.browse-btn {
width: 100%;
padding: 1.2rem;
font-size: 1.3rem;
}
}
@media (max-width: 480px) {
.account-container {
padding: 0.75rem;
}
.account-title {
font-size: 1.8rem;
}
.account-subtitle {
font-size: 1.1rem;
}
.account-tab {
font-size: 1.2rem;
padding: 0.9rem;
}
.tab-content {
padding: 1rem;
}
.overview-card {
padding: 1.2rem;
}
.purchase-card {
padding: 1.2rem;
}
.purchase-title {
font-size: 1.3rem;
}
.form-input {
font-size: 16px !important;
padding: 0.9rem;
}
.credits-overview {
padding: 1rem;
}
.credits-balance {
font-size: 2.5rem;
color: white !important;
}
.social-connection-card {
padding: 1rem;
flex-direction: column;
gap: 1rem;
}
.social-info {
flex-direction: column;
text-align: center;
}
.social-actions {
width: 100%;
}
.connect-btn,
.disconnect-btn {
width: 100%;
padding: 1rem;
}
.notification-option,
.privacy-option,
.sharing-option {
flex-direction: column;
gap: 1rem;
}
.notification-info,
.privacy-info,
.sharing-info {
text-align: left;
}
.toggle-switch {
align-self: flex-start;
}
}
/* Additional mobile improvements */
@media (max-width: 768px) {
.credits-overview {
text-align: center;
padding: 2rem 1.5rem;
}
.buy-credits-btn {
width: 100%;
margin-top: 1rem;
}
.transactions-list {
gap: 1rem;
}
.transaction-item {
flex-direction: column;
align-items: flex-start;
gap: 0.75rem;
}
.transaction-amount {
text-align: left;
width: 100%;
}
.social-connections {
gap: 1rem;
}
.social-connection-card {
padding: 1.5rem;
}
.notification-options,
.privacy-options,
.sharing-options {
gap: 1.5rem;
}
.data-actions {
flex-direction: column;
gap: 1rem;
}
.export-btn,
.delete-data-btn {
width: 100%;
padding: 1.2rem;
}
/* Payment methods mobile */
.cards-grid {
grid-template-columns: 1fr;
gap: 1rem;
}
.card-item {
padding: 1.5rem;
}
.card-actions {
margin-top: 1rem;
}
.remove-card-btn {
width: 100%;
padding: 1rem;
}
/* Subscription mobile */
.subscription-overview {
padding: 1.5rem;
}
/* Prevent horizontal scroll */
* {
max-width: 100%;
}
html, body {
overflow-x: hidden;
}
}
.purchase-artist {
font-size: 1rem;
}
.purchase-info {
flex-wrap: wrap;
font-size: 1rem;
gap: 0.8rem;
}
}
/* Credits Tab Styles */
.credits-overview {
background: linear-gradient(135deg, #667eea, #764ba2);
border-radius: 16px;
padding: 3rem;
text-align: center;
margin-bottom: 3rem;
color: white;
}
.credits-balance {
font-size: 4rem;
font-weight: 700;
margin-bottom: 1rem;
color: white !important;
}
.credits-label {
font-size: 1.6rem;
opacity: 0.9;
margin-bottom: 2rem;
color: white !important;
}
.buy-credits-btn {
background: rgba(255, 255, 255, 0.2);
color: white;
border: 2px solid rgba(255, 255, 255, 0.3);
padding: 1rem 2rem;
border-radius: 8px;
font-size: 1.4rem;
font-weight: 600;
cursor: pointer;
transition: all 0.3s ease;
text-decoration: none;
}
.buy-credits-btn:hover {
background: rgba(255, 255, 255, 0.3);
transform: translateY(-2px);
}
.transactions-list {
background: rgba(255, 255, 255, 0.05);
border-radius: 12px;
overflow: hidden;
}
.transaction-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 1.5rem 2rem;
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
transition: all 0.3s ease;
}
.transaction-item:hover {
background: rgba(255, 255, 255, 0.05);
}
.transaction-item:last-child {
border-bottom: none;
}
.transaction-info {
flex: 1;
}
.transaction-type {
font-size: 1.4rem;
font-weight: 600;
color: white;
margin-bottom: 0.5rem;
}
.transaction-description {
font-size: 1.2rem;
color: #a0aec0;
}
.transaction-date {
font-size: 1.1rem;
color: #a0aec0;
}
.transaction-amount {
font-size: 1.6rem;
font-weight: 600;
text-align: right;
color: white;
}
.amount-positive {
color: #48bb78;
}
.amount-negative {
color: #f56565;
}
/* Settings Tab Styles */
.settings-section {
margin-bottom: 3rem;
}
.settings-section h3 {
font-size: 2rem;
color: white;
margin-bottom: 1rem;
display: flex;
align-items: center;
gap: 1rem;
}
.settings-section p {
font-size: 1.4rem;
color: #a0aec0;
margin-bottom: 1.5rem;
}
.form-group {
margin-bottom: 2rem;
}
.form-label {
display: block;
font-size: 1.6rem;
font-weight: 600;
color: white;
margin-bottom: 1rem;
}
.form-input {
width: 100%;
padding: 1.2rem;
background: rgba(255, 255, 255, 0.05);
border: 1px solid rgba(255, 255, 255, 0.1);
border-radius: 8px;
color: white;
font-size: 1.4rem;
transition: all 0.3s ease;
}
.form-input:focus {
outline: none;
border-color: #667eea;
box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.2);
}
/* URL Input Styling */
.url-input-group {
display: flex;
align-items: center;
background: rgba(255, 255, 255, 0.05);
border: 2px solid rgba(255, 255, 255, 0.1);
border-radius: 8px;
overflow: hidden;
transition: all 0.3s ease;
}
.url-input-group:focus-within {
border-color: #667eea;
box-shadow: 0 0 20px rgba(102, 126, 234, 0.3);
}
.url-prefix {
background: rgba(102, 126, 234, 0.1);
color: #667eea;
padding: 1rem 1.5rem;
font-weight: 600;
border-right: 1px solid rgba(255, 255, 255, 0.1);
white-space: nowrap;
}
.url-input {
border: none;
background: transparent;
flex: 1;
padding: 1rem 1.5rem;
color: white;
font-size: 1.4rem;
}
.url-input:focus {
outline: none;
box-shadow: none;
}
.form-help {
color: #a0aec0;
font-size: 1.2rem;
margin-top: 0.5rem;
display: block;
}
.save-btn {
background: linear-gradient(135deg, #667eea, #764ba2);
color: white;
border: none;
padding: 1.2rem 3rem;
border-radius: 8px;
font-size: 1.6rem;
font-weight: 600;
cursor: pointer;
transition: all 0.3s ease;
}
.save-btn:hover {
transform: translateY(-2px);
box-shadow: 0 8px 25px rgba(102, 126, 234, 0.4);
}
.empty-state {
text-align: center;
padding: 4rem;
color: #a0aec0;
}
.empty-state i {
font-size: 4rem;
margin-bottom: 1rem;
color: #667eea;
}
.empty-state h3 {
font-size: 2rem;
color: white;
margin-bottom: 1rem;
}
.empty-state p {
font-size: 1.4rem;
margin-bottom: 2rem;
}
.browse-btn {
background: linear-gradient(135deg, #667eea, #764ba2);
color: white;
padding: 1rem 2rem;
border-radius: 8px;
text-decoration: none;
font-weight: 600;
transition: all 0.3s ease;
}
.browse-btn:hover {
transform: translateY(-2px);
box-shadow: 0 8px 25px rgba(102, 126, 234, 0.4);
}
/* Subscription Tab Styles */
.subscription-overview {
margin-bottom: 2rem;
}
.subscription-card {
background: linear-gradient(135deg, #667eea, #764ba2);
border-radius: 16px;
padding: 2rem;
color: white;
}
.subscription-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 2rem;
}
.plan-name {
font-size: 2rem;
font-weight: 700;
}
.plan-status {
background: rgba(255, 255, 255, 0.2);
padding: 0.5rem 1rem;
border-radius: 8px;
font-size: 1.2rem;
font-weight: 600;
}
.plan-status.active {
background: #48bb78;
}
.subscription-features {
margin-bottom: 2rem;
}
.feature-item {
display: flex;
align-items: center;
gap: 1rem;
margin-bottom: 1rem;
font-size: 1.4rem;
}
.feature-item i {
color: #48bb78;
}
.subscription-actions {
display: flex;
gap: 1rem;
}
.upgrade-btn, .cancel-btn {
flex: 1;
padding: 1rem;
border: none;
border-radius: 8px;
font-size: 1.4rem;
font-weight: 600;
cursor: pointer;
transition: all 0.3s ease;
}
.upgrade-btn {
background: rgba(255, 255, 255, 0.2);
color: white;
border: 2px solid rgba(255, 255, 255, 0.3);
}
.upgrade-btn:hover {
background: rgba(255, 255, 255, 0.3);
transform: translateY(-2px);
}
.cancel-btn {
background: rgba(220, 53, 69, 0.8);
color: white;
}
.cancel-btn:hover {
background: rgba(220, 53, 69, 1);
transform: translateY(-2px);
}
.billing-history {
background: rgba(255, 255, 255, 0.05);
border-radius: 12px;
overflow: hidden;
}
.billing-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 1.5rem 2rem;
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
}
.billing-item:last-child {
border-bottom: none;
}
.billing-date {
font-size: 1.4rem;
font-weight: 600;
color: white;
margin-bottom: 0.5rem;
}
.billing-description {
font-size: 1.2rem;
color: #a0aec0;
}
.billing-amount {
font-size: 1.6rem;
font-weight: 600;
color: white;
}
/* Payment Methods Tab Styles */
.payment-methods {
margin-bottom: 2rem;
}
.payment-method-card {
background: rgba(255, 255, 255, 0.05);
border: 1px solid rgba(255, 255, 255, 0.1);
border-radius: 12px;
padding: 2rem;
margin-bottom: 1rem;
display: flex;
justify-content: space-between;
align-items: center;
}
.payment-method-info {
display: flex;
align-items: center;
gap: 1.5rem;
}
.card-icon {
font-size: 2.4rem;
color: #667eea;
}
.card-details {
flex: 1;
}
.card-number {
font-size: 1.6rem;
font-weight: 600;
color: white;
margin-bottom: 0.5rem;
}
.card-expiry {
font-size: 1.2rem;
color: #a0aec0;
}
.card-default {
background: #48bb78;
color: white;
padding: 0.3rem 0.8rem;
border-radius: 6px;
font-size: 1rem;
font-weight: 600;
}
.payment-method-actions {
display: flex;
gap: 1rem;
}
.edit-btn, .remove-btn {
padding: 0.8rem 1.5rem;
border: none;
border-radius: 6px;
font-size: 1.2rem;
font-weight: 600;
cursor: pointer;
transition: all 0.3s ease;
}
.edit-btn {
background: rgba(255, 255, 255, 0.1);
color: #a0aec0;
border: 1px solid rgba(255, 255, 255, 0.2);
}
.edit-btn:hover {
background: rgba(255, 255, 255, 0.2);
color: white;
}
.remove-btn {
background: rgba(220, 53, 69, 0.8);
color: white;
}
.remove-btn:hover {
background: rgba(220, 53, 69, 1);
}
.add-payment-btn {
background: linear-gradient(135deg, #667eea, #764ba2);
color: white;
border: none;
padding: 1.2rem 2rem;
border-radius: 8px;
font-size: 1.4rem;
font-weight: 600;
cursor: pointer;
transition: all 0.3s ease;
display: flex;
align-items: center;
gap: 0.8rem;
}
.add-payment-btn:hover {
transform: translateY(-2px);
box-shadow: 0 8px 25px rgba(102, 126, 234, 0.4);
}
.form-row {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 1.5rem;
}
/* Privacy Tab Styles */
.privacy-options {
display: flex;
flex-direction: column;
gap: 2rem;
}
.privacy-option {
display: flex;
justify-content: space-between;
align-items: center;
padding: 1.5rem;
background: rgba(255, 255, 255, 0.05);
border-radius: 12px;
border: 1px solid rgba(255, 255, 255, 0.1);
}
.privacy-info h4 {
font-size: 1.6rem;
color: white;
margin-bottom: 0.5rem;
}
.privacy-info p {
font-size: 1.2rem;
color: #a0aec0;
}
.privacy-select {
background: rgba(255, 255, 255, 0.05);
border: 1px solid rgba(255, 255, 255, 0.1);
color: white;
padding: 0.8rem 1.2rem;
border-radius: 8px;
font-size: 1.4rem;
min-width: 150px;
}
.privacy-select:focus {
outline: none;
border-color: #667eea;
}
.toggle-switch {
position: relative;
display: inline-block;
width: 60px;
height: 34px;
}
.toggle-switch input {
opacity: 0;
width: 0;
height: 0;
}
.toggle-slider {
position: absolute;
cursor: pointer;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(255, 255, 255, 0.1);
transition: 0.4s;
border-radius: 34px;
}
.toggle-slider:before {
position: absolute;
content: "";
height: 26px;
width: 26px;
left: 4px;
bottom: 4px;
background-color: white;
transition: 0.4s;
border-radius: 50%;
}
input:checked + .toggle-slider {
background-color: #667eea;
}
input:checked + .toggle-slider:before {
transform: translateX(26px);
}
.data-actions {
display: flex;
gap: 1rem;
flex-wrap: wrap;
}
.export-btn, .delete-data-btn {
padding: 1rem 2rem;
border: none;
border-radius: 8px;
font-size: 1.4rem;
font-weight: 600;
cursor: pointer;
transition: all 0.3s ease;
display: flex;
align-items: center;
gap: 0.8rem;
}
.export-btn {
background: linear-gradient(135deg, #667eea, #764ba2);
color: white;
}
.export-btn:hover {
transform: translateY(-2px);
box-shadow: 0 8px 25px rgba(102, 126, 234, 0.4);
}
.delete-data-btn {
background: rgba(220, 53, 69, 0.8);
color: white;
}
.delete-data-btn:hover {
background: rgba(220, 53, 69, 1);
transform: translateY(-2px);
}
/* Notifications Tab Styles */
.notification-options {
display: flex;
flex-direction: column;
gap: 2rem;
}
.notification-option {
display: flex;
justify-content: space-between;
align-items: center;
padding: 1.5rem;
background: rgba(255, 255, 255, 0.05);
border-radius: 12px;
border: 1px solid rgba(255, 255, 255, 0.1);
}
.notification-info h4 {
font-size: 1.6rem;
color: white;
margin-bottom: 0.5rem;
}
.notification-info p {
font-size: 1.2rem;
color: #a0aec0;
}
/* Social Connections Tab Styles */
.social-connections {
display: flex;
flex-direction: column;
gap: 1.5rem;
}
.social-connection-card {
background: rgba(255, 255, 255, 0.05);
border: 1px solid rgba(255, 255, 255, 0.1);
border-radius: 12px;
padding: 2rem;
display: flex;
justify-content: space-between;
align-items: center;
transition: all 0.3s ease;
}
.social-connection-card:hover {
border-color: rgba(102, 126, 234, 0.3);
transform: translateY(-2px);
}
.social-info {
display: flex;
align-items: center;
gap: 1.5rem;
}
.social-icon {
font-size: 2.4rem;
width: 60px;
height: 60px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
color: white;
}
.social-icon .fa-google { background: #DB4437; }
.social-icon .fa-facebook { background: #4267B2; }
.social-icon .fa-twitter { background: #1DA1F2; }
.social-icon .fa-apple { background: #000000; }
.social-icon .fa-discord { background: #7289DA; }
.social-icon .fa-github { background: #333333; }
.social-details {
flex: 1;
}
.social-name {
font-size: 1.6rem;
font-weight: 600;
color: white;
margin-bottom: 0.5rem;
}
.social-email {
font-size: 1.2rem;
color: #a0aec0;
}
.connection-status {
padding: 0.5rem 1rem;
border-radius: 6px;
font-size: 1.2rem;
font-weight: 600;
}
.connection-status.connected {
background: #48bb78;
color: white;
}
.connection-status.disconnected {
background: rgba(255, 255, 255, 0.1);
color: #a0aec0;
}
.social-actions {
display: flex;
gap: 1rem;
}
.connect-btn, .disconnect-btn {
padding: 0.8rem 1.5rem;
border: none;
border-radius: 6px;
font-size: 1.2rem;
font-weight: 600;
cursor: pointer;
transition: all 0.3s ease;
display: flex;
align-items: center;
gap: 0.5rem;
}
.connect-btn {
background: linear-gradient(135deg, #667eea, #764ba2);
color: white;
}
.connect-btn:hover {
transform: translateY(-2px);
box-shadow: 0 8px 25px rgba(102, 126, 234, 0.4);
}
.disconnect-btn {
background: rgba(220, 53, 69, 0.8);
color: white;
}
.disconnect-btn:hover {
background: rgba(220, 53, 69, 1);
transform: translateY(-2px);
}
.sharing-options {
display: flex;
flex-direction: column;
gap: 2rem;
}
.sharing-option {
display: flex;
justify-content: space-between;
align-items: center;
padding: 1.5rem;
background: rgba(255, 255, 255, 0.05);
border-radius: 12px;
border: 1px solid rgba(255, 255, 255, 0.1);
}
.sharing-info h4 {
font-size: 1.6rem;
color: white;
margin-bottom: 0.5rem;
}
.sharing-info p {
font-size: 1.2rem;
color: #a0aec0;
}
/* History Table Styles */
.history-table {
width: 100%;
border-collapse: collapse;
margin-top: 1rem;
background: rgba(255, 255, 255, 0.05);
border-radius: 12px;
overflow: hidden;
}
.history-table th, .history-table td {
padding: 1.2rem 1.5rem;
text-align: left;
font-size: 1.4rem;
color: #a0aec0;
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
}
.history-table th {
font-weight: 600;
color: white;
background: rgba(255, 255, 255, 0.08);
}
.history-table tr:last-child td {
border-bottom: none;
}
.history-table tbody tr:hover {
background: rgba(255, 255, 255, 0.05);
}
.history-table .amount-positive {
color: #48bb78;
}
.history-table .amount-negative {
color: #f56565;
}
</style>
<div class="account-container">
<div class="account-header">
<h1 class="account-title"><?= t('account.title') ?></h1>
<p class="account-subtitle"><?= t('account.subtitle') ?></p>
</div>
<!-- Account Overview -->
<div class="account-overview">
<div class="overview-card">
<div class="overview-icon">
<i class="fas fa-coins"></i>
</div>
<div class="overview-number"><?= $user['credits'] ?></div>
<div class="overview-label"><?= t('account.available_credits') ?></div>
</div>
<div class="overview-card">
<div class="overview-icon">
<i class="fas fa-shopping-bag"></i>
</div>
<div class="overview-number">
<?php
// Final safety check: ensure count is always accurate
$final_count = isset($user['total_purchases']) ? intval($user['total_purchases']) : 0;
// If on purchases tab and we have the purchases array, use that count (most reliable)
if ($current_tab === 'purchases' && isset($purchases) && is_array($purchases)) {
$final_count = count($purchases);
}
echo $final_count;
?>
</div>
<div class="overview-label"><?= t('account.total_purchases') ?></div>
</div>
<div class="overview-card">
<div class="overview-icon">
<i class="fas fa-ticket-alt"></i>
</div>
<div class="overview-number">
<?php
$ticket_count_display = isset($user['total_tickets']) ? intval($user['total_tickets']) : 0;
if ($current_tab === 'tickets' && isset($tickets_list) && is_array($tickets_list)) {
$ticket_count_display = count($tickets_list);
}
echo $ticket_count_display;
?>
</div>
<div class="overview-label"><?= t('account.total_tickets') ?></div>
</div>
<div class="overview-card">
<div class="overview-icon">
<i class="fas fa-calendar-alt"></i>
</div>
<div class="overview-number">
<?php
if (!empty($user['created_at']) && $user['created_at'] !== '0000-00-00 00:00:00') {
$member_date = strtotime($user['created_at']);
if ($member_date && $member_date > 0) {
echo date('M Y', $member_date);
} else {
echo 'N/A';
}
} else {
echo 'N/A';
}
?>
</div>
<div class="overview-label"><?= t('account.member_since') ?></div>
</div>
<div class="overview-card">
<div class="overview-icon">
<i class="fas fa-crown"></i>
</div>
<div class="overview-number">
<?php
// FIX: Use getEffectivePlan() to get the correct plan (checks subscription first, then users.plan)
try {
require_once __DIR__ . '/utils/subscription_helpers.php';
$effective_plan = getEffectivePlan($_SESSION['user_id']);
echo ucfirst($effective_plan);
} catch (Exception $e) {
error_log("Error getting effective plan in overview: " . $e->getMessage());
// Fallback to users.plan
$plan = !empty($user['plan']) ? $user['plan'] : 'free';
echo ucfirst($plan);
}
?>
</div>
<div class="overview-label"><?= t('account.current_plan') ?></div>
</div>
</div>
<div class="account-tabs">
<a href="?tab=purchases" class="account-tab <?= $current_tab === 'purchases' ? 'active' : '' ?>">
<i class="fas fa-shopping-bag"></i>
<?= t('account.my_purchases') ?>
</a>
<a href="?tab=tickets" class="account-tab <?= $current_tab === 'tickets' ? 'active' : '' ?>">
<i class="fas fa-ticket-alt"></i>
<?= t('account.my_tickets') ?>
</a>
<a href="?tab=credits" class="account-tab <?= $current_tab === 'credits' ? 'active' : '' ?>">
<i class="fas fa-coins"></i>
<?= t('account.credits_transactions') ?>
</a>
<a href="?tab=subscription" class="account-tab <?= $current_tab === 'subscription' ? 'active' : '' ?>">
<i class="fas fa-crown"></i>
<?= t('account.subscription') ?>
</a>
<a href="?tab=payment" class="account-tab <?= $current_tab === 'payment' ? 'active' : '' ?>">
<i class="fas fa-credit-card"></i>
<?= t('account.payment_methods') ?>
</a>
<a href="?tab=privacy" class="account-tab <?= $current_tab === 'privacy' ? 'active' : '' ?>">
<i class="fas fa-user-secret"></i>
<?= t('account.privacy') ?>
</a>
<a href="?tab=notifications" class="account-tab <?= $current_tab === 'notifications' ? 'active' : '' ?>">
<i class="fas fa-bell"></i>
<?= t('account.notifications') ?>
</a>
<a href="?tab=connections" class="account-tab <?= $current_tab === 'connections' ? 'active' : '' ?>">
<i class="fas fa-link"></i>
<?= t('account.connections') ?>
</a>
<a href="?tab=settings" class="account-tab <?= $current_tab === 'settings' ? 'active' : '' ?>">
<i class="fas fa-cog"></i>
<?= t('account.account_settings') ?>
</a>
<a href="?tab=security" class="account-tab <?= $current_tab === 'security' ? 'active' : '' ?>">
<i class="fas fa-shield-alt"></i>
<?= t('account.security') ?>
</a>
</div>
<div class="tab-content">
<?php if ($current_tab === 'purchases'): ?>
<!-- Purchases Tab -->
<?php if (empty($purchases)): ?>
<div class="empty-state">
<i class="fas fa-music"></i>
<h3><?= t('account.no_purchases') ?></h3>
<p><?= t('account.no_purchases_desc') ?></p>
<a href="artists.php" class="browse-btn">
<i class="fas fa-search"></i>
<?= t('account.browse_artists') ?>
</a>
</div>
<?php else: ?>
<div class="purchases-grid">
<?php foreach ($purchases as $purchase): ?>
<div class="purchase-card">
<div class="purchase-header">
<div>
<div class="purchase-title">
<a href="/track.php?id=<?= $purchase['track_id'] ?>" class="purchase-title-link"><?= htmlspecialchars($purchase['title']) ?></a>
</div>
<div class="purchase-artist">
<?= t('account.by') ?> <a href="/artist_profile.php?id=<?= $purchase['artist_id'] ?>" class="purchase-artist-link"><?= htmlspecialchars($purchase['artist_name']) ?></a>
</div>
</div>
<div class="purchase-price">$<?= number_format($purchase['price_paid'], 2) ?></div>
</div>
<div class="purchase-info">
<span><i class="fas fa-clock"></i> <?= $purchase['duration'] ? gmdate("i:s", $purchase['duration']) : t('account.unknown') ?></span>
<span><i class="fas fa-music"></i> <?= ucfirst($purchase['music_type']) ?></span>
<span><i class="fas fa-download"></i> <?= $purchase['download_count'] ?> <?= t('account.downloads') ?></span>
</div>
<div class="purchase-date">
<?= t('account.purchased_on') ?> <?= date('M j, Y', strtotime($purchase['purchase_date'])) ?>
</div>
<?php
// Parse metadata for filename generation
$metadata = [];
if (!empty($purchase['metadata'])) {
$metadata = is_string($purchase['metadata']) ? json_decode($purchase['metadata'], true) : $purchase['metadata'];
}
$genre = $metadata['genre'] ?? '';
$bpm = $metadata['bpm'] ?? '';
$key = $metadata['key'] ?? '';
$mood = $metadata['mood'] ?? '';
// Extract numerical key from prompt if available
$numericalKey = '';
if (!empty($purchase['prompt'])) {
if (preg_match('/key[:\s]+([0-9]+[A-G]?)\s*[–\-]?\s*/i', $purchase['prompt'], $keyMatch)) {
$numericalKey = $keyMatch[1] ?? '';
}
}
// Prepare data for JavaScript
$trackData = [
'title' => $purchase['title'],
'artist' => $purchase['artist_name'],
'genre' => $genre,
'bpm' => $bpm,
'key' => $key,
'numericalKey' => $numericalKey,
'mood' => $mood,
'audio_url' => $purchase['audio_url'],
'purchase_id' => $purchase['purchase_id']
];
?>
<div class="purchase-actions">
<button class="purchase-btn play-btn" onclick="playTrack('<?= htmlspecialchars($purchase['audio_url']) ?>', '<?= htmlspecialchars($purchase['title']) ?>')">
<i class="fas fa-play"></i>
<?= t('account.play') ?>
</button>
<div class="download-options">
<?php
// Get second variation for play button (index 1, which is the second variation)
$secondVariation = null;
if (!empty($purchase['variations']) && count($purchase['variations']) > 1) {
$secondVariation = $purchase['variations'][1] ?? null;
}
?>
<?php if ($secondVariation): ?>
<button class="purchase-btn play-btn" onclick="playTrack('<?= htmlspecialchars($secondVariation['audio_url']) ?>', '<?= htmlspecialchars($purchase['title']) ?>')">
<i class="fas fa-play"></i>
<?= t('account.play_second_variation') ?>
</button>
<?php endif; ?>
<?php if (!empty($purchase['variations'])): ?>
<div class="variations-downloads">
<div class="variations-label"><?= t('account.variations') ?></div>
<?php foreach ($purchase['variations'] as $variation): ?>
<?php
$variationData = [
'title' => $purchase['title'],
'artist' => $purchase['artist_name'],
'genre' => $genre,
'bpm' => $bpm,
'key' => $key,
'numericalKey' => $numericalKey,
'mood' => $mood,
'audio_url' => $variation['audio_url'],
'purchase_id' => $purchase['purchase_id'],
'variation_index' => $variation['variation_index'],
'variation_id' => $variation['id'],
'variation_title' => $variation['title'] ?? null,
'total_variations' => count($purchase['variations'])
];
?>
<button class="purchase-btn download-btn" onclick="downloadTrackWithMetadata(<?= htmlspecialchars(json_encode($variationData)) ?>, 'variation')">
<i class="fas fa-download"></i>
<?= t('account.download') ?>
</button>
<?php endforeach; ?>
</div>
<?php endif; ?>
</div>
</div>
</div>
<?php endforeach; ?>
</div>
<?php endif; ?>
<?php elseif ($current_tab === 'tickets'): ?>
<?php if (empty($tickets_list)): ?>
<div class="tickets-empty">
<i class="fas fa-ticket-alt"></i>
<h3><?= t('account.no_tickets') ?></h3>
<p><?= t('account.no_tickets_desc') ?></p>
<a href="/events.php" class="browse-btn">
<i class="fas fa-calendar-plus"></i>
<?= t('tickets.browse_events') ?>
</a>
</div>
<?php else: ?>
<p class="tab-lead"><?= t('account.tickets_help') ?></p>
<div class="tickets-grid">
<?php foreach ($tickets_list as $ticket): ?>
<?php
$statusSlug = strtolower($ticket['status'] ?? 'confirmed');
$statusKey = 'tickets.status_' . $statusSlug;
$statusLabel = t($statusKey);
if ($statusLabel === $statusKey) {
$statusLabel = ucfirst($statusSlug);
}
$dateLabel = formatEventDateRange($ticket['start_date'] ?? null, $ticket['end_date'] ?? null);
$locationParts = array_filter([
$ticket['venue_name'] ?? '',
$ticket['address'] ?? '',
$ticket['location'] ?? ''
]);
$locationLabel = implode(', ', $locationParts);
$calendarLink = buildCalendarLink($ticket['title'], $ticket['start_date'] ?? null, $ticket['end_date'] ?? null, $locationLabel);
?>
<div class="ticket-card">
<div class="ticket-header">
<div>
<div class="ticket-title"><?= htmlspecialchars($ticket['title']) ?></div>
<div class="ticket-organizer"><?= t('account.organized_by') ?> <?= htmlspecialchars($ticket['organizer_name']) ?></div>
</div>
<span class="ticket-status <?= htmlspecialchars($statusSlug) ?>"><?= htmlspecialchars($statusLabel) ?></span>
</div>
<div class="ticket-info">
<?php if (!empty($dateLabel)): ?>
<span><i class="fas fa-calendar"></i><?= htmlspecialchars($dateLabel) ?></span>
<?php endif; ?>
<?php if (!empty($locationLabel)): ?>
<span><i class="fas fa-map-marker-alt"></i><?= htmlspecialchars($locationLabel) ?></span>
<?php endif; ?>
<span><i class="fas fa-barcode"></i><?= t('account.ticket_code') ?>: <strong><?= htmlspecialchars($ticket['ticket_code']) ?></strong></span>
</div>
<div class="ticket-actions">
<a href="/event_details.php?id=<?= $ticket['event_id'] ?>" class="ticket-btn">
<i class="fas fa-eye"></i>
<?= t('account.view_event') ?>
</a>
<a href="/generate_ticket_qr.php?code=<?= urlencode($ticket['ticket_code']) ?>" class="ticket-btn primary" target="_blank" rel="noopener">
<i class="fas fa-qrcode"></i>
<?= t('account.view_qr') ?>
</a>
<button class="ticket-btn" onclick="copyTicketCode('<?= htmlspecialchars($ticket['ticket_code'], ENT_QUOTES, 'UTF-8') ?>', this)">
<i class="fas fa-copy"></i>
<?= t('tickets.copy_code') ?>
</button>
<a href="<?= htmlspecialchars($calendarLink) ?>" target="_blank" rel="noopener" class="ticket-btn">
<i class="fas fa-calendar-plus"></i>
<?= t('account.add_to_calendar') ?>
</a>
</div>
</div>
<?php endforeach; ?>
</div>
<?php endif; ?>
<?php elseif ($current_tab === 'credits'): ?>
<!-- Credits Tab -->
<div class="credits-overview">
<div class="credits-balance"><?= $user['credits'] ?></div>
<div class="credits-label"><?= t('account.available_credits') ?></div>
<a href="credits.php#extra-credits" class="buy-credits-btn">
<i class="fas fa-plus"></i>
<?= t('account.buy_more_credits') ?>
</a>
</div>
<!-- Subscription Requirement Notice -->
<div style="background: linear-gradient(135deg, rgba(102, 126, 234, 0.1), rgba(118, 75, 162, 0.1)); border: 1px solid rgba(102, 126, 234, 0.3); border-radius: 12px; padding: 2rem; margin-bottom: 3rem;">
<div style="display: flex; align-items: center; gap: 1rem; margin-bottom: 1rem;">
<i class="fas fa-info-circle" style="font-size: 2rem; color: #667eea;"></i>
<h3 style="margin: 0; font-size: 1.8rem; color: #fff;"><?= t('home.credits_subscription_required_title') ?></h3>
</div>
<p style="margin: 0; font-size: 1.4rem; color: #a0aec0; line-height: 1.6;">
<?= t('home.credits_subscription_required_message') ?>
</p>
</div>
<?php if ($subscription_info): ?>
<?php
$plans_config = require __DIR__ . '/config/subscription_plans.php';
// FIX: Use getEffectivePlan() to ensure correct plan is displayed
require_once __DIR__ . '/utils/subscription_helpers.php';
$effective_plan_credits = getEffectivePlan($_SESSION['user_id']);
$plan_info = isset($plans_config[$effective_plan_credits]) ? $plans_config[$effective_plan_credits] : null;
?>
<div style="background: #2a2a2a; border-radius: 12px; padding: 20px; margin-bottom: 30px; border: 1px solid rgba(255, 255, 255, 0.1);">
<h3 style="color: white; margin-bottom: 15px; display: flex; align-items: center; gap: 10px;">
<i class="fas fa-crown" style="color: <?= $plan_info['color'] ?? '#48bb78' ?>;"></i>
Active Subscription
</h3>
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 15px;">
<div>
<div style="color: #a0aec0; font-size: 0.9rem; margin-bottom: 5px;">Plan</div>
<div style="color: white; font-size: 1.1rem; font-weight: bold;"><?= ucfirst($effective_plan_credits) ?></div>
<?php if ($plan_info): ?>
<div style="color: <?= $plan_info['color'] ?>; font-size: 0.9rem; margin-top: 5px;">
$<?= number_format($plan_info['price'], 2) ?>/month
</div>
<?php endif; ?>
</div>
<div>
<div style="color: #a0aec0; font-size: 0.9rem; margin-bottom: 5px;">Status</div>
<div style="color: <?= $subscription_info['status'] === 'active' ? '#48bb78' : '#ffc107'; ?>; font-size: 1.1rem; font-weight: bold;">
<?= ucfirst($subscription_info['status']) ?>
</div>
</div>
<?php if ($subscription_usage && $plan_info): ?>
<div>
<div style="color: #a0aec0; font-size: 0.9rem; margin-bottom: 5px;">Monthly Usage</div>
<div style="color: white; font-size: 1.1rem; font-weight: bold;">
<?= $subscription_usage['tracks_created'] ?> / <?= $subscription_usage['track_limit'] ?> tracks
</div>
</div>
<?php endif; ?>
<div>
<div style="color: #a0aec0; font-size: 0.9rem; margin-bottom: 5px;">
<i class="fas fa-calendar-check"></i> <?= t('subscription.renews') ?>
</div>
<div style="color: #48bb78; font-size: 1.3rem; font-weight: bold; display: flex; align-items: center; gap: 10px; flex-wrap: wrap;">
<?php if ($subscription_info['current_period_end']):
$period_end = strtotime($subscription_info['current_period_end']);
$now = time();
$days_remaining = max(0, floor(($period_end - $now) / 86400));
$formatted_date = date('F j, Y', $period_end);
?>
<span style="background: rgba(72, 187, 120, 0.2); border: 1px solid rgba(72, 187, 120, 0.4); padding: 6px 12px; border-radius: 6px;">
<i class="fas fa-clock"></i> <?= $formatted_date ?>
</span>
<?php if ($days_remaining > 0): ?>
<span style="font-size: 1.1rem; color: #a0aec0; font-weight: 500;">
(<?= $days_remaining ?> <?= $days_remaining === 1 ? t('subscription.day_remaining') : t('subscription.days_remaining') ?>)
</span>
<?php endif; ?>
<?php else: ?>
N/A
<?php endif; ?>
</div>
</div>
</div>
<div style="margin-top: 15px;">
<a href="account_settings.php?tab=subscription" style="display: inline-block; padding: 10px 20px; background: <?= $plan_info['color'] ?? '#48bb78' ?>; color: white; text-decoration: none; border-radius: 8px; font-weight: 600;">
Manage Subscription
</a>
</div>
</div>
<?php endif; ?>
<h3><i class="fas fa-history"></i> <?= t('account.transaction_history') ?></h3>
<?php if (empty($transactions)): ?>
<div class="empty-state">
<i class="fas fa-receipt"></i>
<h3><?= t('account.no_transactions') ?></h3>
<p><?= t('account.no_transactions_desc') ?></p>
</div>
<?php else: ?>
<div class="transactions-list">
<?php foreach ($transactions as $transaction): ?>
<div class="transaction-item">
<div class="transaction-info">
<div class="transaction-type"><?= htmlspecialchars($transaction['type_display']) ?></div>
<div class="transaction-description"><?= htmlspecialchars($transaction['description']) ?></div>
<div class="transaction-date"><?= date('M j, Y g:i A', strtotime($transaction['created_at'])) ?></div>
</div>
<div class="transaction-amount <?= $transaction['amount'] >= 0 ? 'amount-positive' : 'amount-negative' ?>">
<?= $transaction['amount'] >= 0 ? '+' : '' ?><?= $transaction['amount'] ?> <?= t('account.credits') ?>
</div>
</div>
<?php endforeach; ?>
</div>
<?php endif; ?>
<?php elseif ($current_tab === 'subscription'): ?>
<!-- Subscription Tab -->
<?php
try {
require_once __DIR__ . '/utils/subscription_helpers.php';
$subscription = getSubscriptionInfo($_SESSION['user_id']);
$usage = null;
$plans_config = require __DIR__ . '/config/subscription_plans.php';
if ($subscription) {
try {
// FIX: Use getEffectivePlan() to get correct plan for usage tracking
$effective_plan_usage = getEffectivePlan($_SESSION['user_id']);
$usage = getMonthlyTrackUsage($_SESSION['user_id'], $effective_plan_usage);
} catch (Exception $e) {
error_log("Error getting usage: " . $e->getMessage());
$usage = ['tracks_created' => 0, 'track_limit' => 0];
}
} else {
$usage = ['tracks_created' => 0, 'track_limit' => 0];
}
} catch (Exception $e) {
error_log("Error in subscription tab: " . $e->getMessage());
$subscription = null;
$usage = ['tracks_created' => 0, 'track_limit' => 0];
}
?>
<div style="text-align: center; padding: 40px 20px;">
<h2 style="color: white; margin-bottom: 30px;"><?= t('account.subscription_management') ?></h2>
<?php if ($subscription): ?>
<?php
// FIX: Use getEffectivePlan() to ensure correct plan is displayed
require_once __DIR__ . '/utils/subscription_helpers.php';
$effective_plan_sub = getEffectivePlan($_SESSION['user_id']);
$plan_info = isset($plans_config[$effective_plan_sub]) ? $plans_config[$effective_plan_sub] : null;
?>
<div style="background: #2a2a2a; border-radius: 12px; padding: 30px; max-width: 600px; margin: 0 auto; text-align: left;">
<div style="display: grid; grid-template-columns: repeat(3, 1fr); gap: 20px; margin-bottom: 30px;">
<div>
<div style="color: #a0aec0; font-size: 0.9rem; margin-bottom: 5px;"><?= t('account.plan') ?></div>
<div style="color: white; font-size: 1.2rem; font-weight: bold;">
<?php
echo ucfirst($effective_plan_sub);
?>
</div>
<?php if ($plan_info): ?>
<div style="color: <?= $plan_info['color'] ?>; font-size: 1rem; margin-top: 5px;">
$<?= number_format($plan_info['price'], 2) ?><?= t('account.per_month') ?>
</div>
<?php endif; ?>
</div>
<div>
<div style="color: #a0aec0; font-size: 0.9rem; margin-bottom: 5px;"><?= t('account.status') ?></div>
<div style="color: <?= $subscription['status'] === 'active' ? '#48bb78' : '#ffc107'; ?>; font-size: 1.2rem; font-weight: bold;">
<?= $subscription['status'] === 'active' ? t('account.active') : t('account.inactive') ?>
</div>
</div>
<div>
<div style="color: #a0aec0; font-size: 0.9rem; margin-bottom: 5px;"><?= t('account.renews') ?></div>
<div style="color: white; font-size: 1.2rem; font-weight: bold;">
<?= $subscription['current_period_end'] ? date('M j, Y', strtotime($subscription['current_period_end'])) : 'N/A' ?>
</div>
</div>
</div>
<?php if ($effective_plan_sub && in_array($effective_plan_sub, ['essential', 'starter', 'pro', 'premium']) && $usage): ?>
<div style="background: #1a1a1a; padding: 20px; border-radius: 8px; margin-bottom: 20px;">
<h3 style="color: white; margin-bottom: 15px;"><?= t('account.monthly_track_usage') ?></h3>
<div style="margin-bottom: 15px;">
<div style="display: flex; justify-content: space-between; margin-bottom: 10px;">
<span style="color: #a0aec0;"><?= t('account.tracks_used') ?></span>
<span style="color: white; font-weight: bold;"><?= $usage['tracks_created'] ?? 0 ?> / <?= $usage['track_limit'] ?? 0 ?></span>
</div>
<div style="background: #2a2a2a; height: 20px; border-radius: 10px; overflow: hidden;">
<div style="background: <?= $plan_info ? $plan_info['color'] : '#667eea' ?>; height: 100%; width: <?= min(100, (($usage['tracks_created'] ?? 0) / max(1, ($usage['track_limit'] ?? 1))) * 100) ?>%; transition: width 0.3s;"></div>
</div>
</div>
<p style="color: #a0aec0; font-size: 0.9rem;">
<?= t('account.resets_on') ?> <strong><?= $subscription['current_period_end'] ? date('F j, Y', strtotime($subscription['current_period_end'])) : t('account.your_next_billing_date') ?></strong>
</p>
</div>
<?php endif; ?>
<div style="text-align: center; margin-top: 20px; display: flex; gap: 15px; justify-content: center; flex-wrap: wrap;">
<a href="/manage_subscription.php" style="display: inline-block; padding: 12px 30px; background: #667eea; color: white; text-decoration: none; border-radius: 8px;">
<?= t('account.manage_subscription') ?>
</a>
<a href="/invoices.php" style="display: inline-block; padding: 12px 30px; background: #48bb78; color: white; text-decoration: none; border-radius: 8px;">
<i class="fas fa-file-invoice"></i> View Invoices
</a>
<a href="/pricing.php" style="display: inline-block; padding: 12px 30px; background: #2a2a2a; color: white; text-decoration: none; border-radius: 8px; border: 1px solid #444;">
<?= t('account.view_all_plans') ?>
</a>
</div>
</div>
<?php else: ?>
<div style="background: #2a2a2a; border-radius: 12px; padding: 40px; max-width: 500px; margin: 0 auto;">
<p style="color: #a0aec0; font-size: 1.1rem; margin-bottom: 30px;"><?= t('account.no_active_subscription') ?></p>
<p style="color: #718096; font-size: 0.95rem; margin-bottom: 30px;"><?= t('account.choose_plans_desc') ?></p>
<a href="/pricing.php" style="display: inline-block; padding: 15px 40px; background: #667eea; color: white; text-decoration: none; border-radius: 8px; font-size: 1.1rem; margin-bottom: 15px;">
<?= t('account.view_all_plans') ?>
</a>
<div style="margin-top: 20px; padding: 1.5rem; background: rgba(102, 126, 234, 0.1); border-radius: 8px; border: 1px solid rgba(102, 126, 234, 0.3);">
<p style="color: #667eea; font-size: 1rem; margin: 0 0 10px 0; font-weight: 600;">
<i class="fas fa-info-circle"></i> <?= t('home.credits_subscription_required_title') ?>
</p>
<p style="color: #a0aec0; font-size: 0.9rem; margin: 0 0 15px 0;">
<?= t('home.credits_subscription_required_message') ?>
</p>
<a href="/credits.php" style="color: #667eea; text-decoration: none; font-size: 0.9rem;">
<?= t('account.or_buy_credits') ?> (<?= t('home.credits_subscription_required_title') ?>)
</a>
</div>
</div>
<?php endif; ?>
<?php if ($upgrade_required): ?>
<div class="upgrade-notice" style="background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; padding: 20px; border-radius: 12px; margin-top: 30px; text-align: center;">
<h3 style="margin: 0 0 10px 0; font-size: 1.5rem;">
<i class="fas fa-lock" style="margin-right: 10px;"></i>
<?= t('account.upgrade_required') ?>
</h3>
<p style="margin: 0; font-size: 1.1rem; opacity: 0.9;">
<?= htmlspecialchars($upgrade_message) ?>
</p>
<div style="margin-top: 15px;">
<a href="/pricing.php" style="background: white; color: #667eea; border: none; padding: 12px 24px; border-radius: 8px; font-weight: 600; cursor: pointer; font-size: 1rem; text-decoration: none; display: inline-block;">
<i class="fas fa-arrow-up" style="margin-right: 8px;"></i>
<?= t('account.upgrade_now') ?>
</a>
</div>
</div>
<?php endif; ?>
</div>
<?php elseif ($current_tab === 'payment'): ?>
<!-- Payment Methods Tab - Redesigned -->
<div class="payment-dashboard">
<!-- Payment Methods Section -->
<div class="payment-section">
<div class="section-header">
<div class="header-content">
<h2><i class="fas fa-credit-card"></i> <?= t('account.payment_methods_title') ?></h2>
<p><?= t('account.manage_cards') ?></p>
</div>
<div class="header-actions">
<button class="refresh-btn" onclick="location.reload()" style="margin-right: 10px; background: #4a5568; border: none; color: white; padding: 0.8rem 1.5rem; border-radius: 8px; font-size: 1rem; font-weight: 600; cursor: pointer;">
<i class="fas fa-sync-alt"></i> Refresh Page
</button>
<button class="ajax-refresh-btn" onclick="refreshPaymentMethods()" style="margin-right: 10px; background: #38a169; border: none; color: white; padding: 0.8rem 1.5rem; border-radius: 8px; font-size: 1rem; font-weight: 600; cursor: pointer;">
<i class="fas fa-sync-alt"></i> AJAX Refresh
</button>
<button class="add-card-btn" onclick="showAddCardModal()">
<i class="fas fa-plus"></i> <?= t('account.add_card') ?>
</button>
</div>
</div>
<div class="cards-container">
<?php
// Debug output
error_log("Payment methods count in template: " . count($payment_methods));
error_log("Customer ID in template: " . ($user['stripe_customer_id'] ?? 'NULL'));
// Show debug info if no cards but customer exists (only in debug mode)
if (isDebugMode() && empty($payment_methods) && !empty($user['stripe_customer_id'])) {
echo '<div style="background: rgba(255, 193, 7, 0.1); border: 1px solid rgba(255, 193, 7, 0.3); border-radius: 8px; padding: 1rem; margin-bottom: 1rem;">';
echo '<p style="color: #ffc107; margin: 0;"><strong>Debug Info:</strong></p>';
echo '<p style="color: #ffc107; margin: 0.5rem 0 0 0;">Customer ID: ' . htmlspecialchars($user['stripe_customer_id']) . '</p>';
echo '<p style="color: #ffc107; margin: 0.5rem 0 0 0;">Payment Methods Found: ' . count($payment_methods) . '</p>';
echo '<p style="color: #ffc107; margin: 0.5rem 0 0 0;">AJAX Enabled: ' . (isAjaxEnabled() ? 'Yes' : 'No') . '</p>';
echo '<p style="color: #ffc107; margin: 0.5rem 0 0 0;">Auto Refresh: ' . (isAutoRefreshPaymentMethods() ? 'Yes' : 'No') . '</p>';
echo '<p style="color: #ffc107; margin: 0.5rem 0 0 0;">Try clicking the Refresh button above to reload payment methods.</p>';
echo '</div>';
}
?>
<?php if (empty($payment_methods)): ?>
<div class="empty-cards">
<div class="empty-icon">
<i class="fas fa-credit-card"></i>
</div>
<h3><?= t('account.no_cards') ?></h3>
<p><?= t('account.add_first_card') ?></p>
<button class="add-first-card-btn" onclick="showAddCardModal()">
<i class="fas fa-plus"></i> <?= t('account.add_first_card') ?>
</button>
</div>
<?php else: ?>
<div class="cards-grid">
<?php foreach ($payment_methods as $pm): ?>
<div class="card-item">
<div class="card-header">
<div class="card-brand">
<i class="fab fa-cc-<?= htmlspecialchars($pm['card']['brand']) ?>"></i>
<span><?= ucfirst(htmlspecialchars($pm['card']['brand'])) ?></span>
</div>
<?php if ($pm['id'] == ($user['default_payment_method_id'] ?? null)): ?>
<div class="default-badge"><?= t('account.default') ?></div>
<?php endif; ?>
</div>
<div class="card-number">
•••• •••• •••• <?= htmlspecialchars($pm['card']['last4']) ?>
</div>
<div class="card-details">
<span><?= t('account.expires') ?> <?= $pm['card']['exp_month'] ?>/<?= $pm['card']['exp_year'] ?></span>
</div>
<div class="card-actions">
<button class="remove-card-btn" onclick="removeCard('<?= $pm['id'] ?>')">
<i class="fas fa-trash"></i> <?= t('account.remove') ?>
</button>
</div>
</div>
<?php endforeach; ?>
</div>
<?php endif; ?>
</div>
</div>
<!-- Payment History Section -->
<div class="payment-section">
<div class="section-header">
<h2><i class="fas fa-history"></i> Payment History</h2>
<p>Track all your transactions and purchases</p>
</div>
<div class="history-container">
<?php if (empty($credit_purchases) && empty($track_purchases)): ?>
<div class="empty-history">
<div class="empty-icon">
<i class="fas fa-receipt"></i>
</div>
<h3>No transactions yet</h3>
<p>Your payment history will appear here once you make your first purchase</p>
</div>
<?php else: ?>
<div class="transactions-list">
<?php foreach ($credit_purchases as $p): ?>
<div class="transaction-item">
<div class="transaction-icon credit-purchase">
<i class="fas fa-coins"></i>
</div>
<div class="transaction-details">
<div class="transaction-title">Credit Purchase</div>
<div class="transaction-subtitle"><?= htmlspecialchars($p['package']) ?> Package</div>
<div class="transaction-date"><?= date('M j, Y', strtotime($p['created_at'])) ?></div>
</div>
<div class="transaction-amount">
<div class="amount">$<?= number_format($p['amount'], 2) ?></div>
<div class="credits">+<?= $p['credits'] ?> credits</div>
</div>
</div>
<?php endforeach; ?>
<?php foreach ($track_purchases as $p): ?>
<div class="transaction-item">
<div class="transaction-icon track-purchase">
<i class="fas fa-music"></i>
</div>
<div class="transaction-details">
<div class="transaction-title">Track Purchase</div>
<div class="transaction-subtitle"><?= htmlspecialchars($p['title']) ?></div>
<div class="transaction-date"><?= date('M j, Y', strtotime($p['purchased_at'])) ?></div>
</div>
<div class="transaction-amount">
<div class="amount">$<?= number_format($p['price_paid'], 2) ?></div>
<div class="credits">-<?= $p['credits_used'] ?> credits</div>
</div>
</div>
<?php endforeach; ?>
</div>
<?php endif; ?>
</div>
</div>
<!-- Credit Usage Section -->
<div class="payment-section">
<div class="section-header">
<h2><i class="fas fa-chart-line"></i> Credit Activity</h2>
<p>Monitor your credit usage and spending patterns</p>
</div>
<div class="credit-container">
<?php if (empty($credit_usage)): ?>
<div class="empty-credits">
<div class="empty-icon">
<i class="fas fa-chart-line"></i>
</div>
<h3>No credit activity</h3>
<p>Your credit usage will appear here when you start using credits</p>
</div>
<?php else: ?>
<div class="credit-activity">
<?php foreach ($credit_usage as $u): ?>
<div class="credit-item">
<div class="credit-icon">
<i class="fas fa-<?= $u['usage_type'] === 'download' ? 'download' : ($u['usage_type'] === 'preview' ? 'play' : 'music') ?>"></i>
</div>
<div class="credit-details">
<div class="credit-action"><?= ucfirst(htmlspecialchars($u['usage_type'])) ?></div>
<div class="credit-track"><?= htmlspecialchars($u['title'] ?? 'Unknown Track') ?></div>
<div class="credit-date"><?= date('M j, Y', strtotime($u['created_at'])) ?></div>
</div>
<div class="credit-amount">
-<?= $u['credits_used'] ?> credits
</div>
</div>
<?php endforeach; ?>
</div>
<?php endif; ?>
</div>
</div>
</div>
<!-- Add Card Modal -->
<div id="addCardModal" class="modal-overlay">
<div class="modal-content">
<div class="modal-header">
<h3><i class="fas fa-credit-card"></i> Add New Card</h3>
<button class="close-modal" onclick="closeAddCardModal()">
<i class="fas fa-times"></i>
</button>
</div>
<form id="addCardForm" class="card-form">
<div class="form-group">
<label>Card Information</label>
<div id="card-element" class="stripe-element"></div>
<div id="card-errors" class="error-message"></div>
</div>
<div class="form-actions">
<button type="button" class="cancel-btn" onclick="closeAddCardModal()">Cancel</button>
<button type="submit" class="save-btn">
<i class="fas fa-save"></i> Save Card
</button>
</div>
</form>
</div>
</div>
<style>
.payment-dashboard {
max-width: 100%;
}
.payment-section {
background: rgba(255, 255, 255, 0.03);
border: 1px solid rgba(255, 255, 255, 0.1);
border-radius: 16px;
padding: 2rem;
margin-bottom: 2rem;
}
.section-header {
display: flex;
justify-content: space-between;
align-items: flex-start;
margin-bottom: 2rem;
}
.header-content h2 {
font-size: 1.8rem;
color: white;
margin: 0 0 0.5rem 0;
display: flex;
align-items: center;
gap: 0.8rem;
}
.header-content p {
color: #a0aec0;
margin: 0;
font-size: 1rem;
}
.add-card-btn {
background: linear-gradient(135deg, #667eea, #764ba2);
color: white;
border: none;
padding: 0.8rem 1.5rem;
border-radius: 8px;
font-size: 1rem;
font-weight: 600;
cursor: pointer;
transition: all 0.3s ease;
display: flex;
align-items: center;
gap: 0.5rem;
}
.add-card-btn:hover {
transform: translateY(-2px);
box-shadow: 0 8px 25px rgba(102, 126, 234, 0.4);
}
.header-actions {
display: flex;
align-items: center;
gap: 10px;
}
.refresh-btn:hover {
background: #2d3748 !important;
transform: translateY(-2px);
}
.empty-cards, .empty-history, .empty-credits {
text-align: center;
padding: 3rem 2rem;
}
.empty-icon {
font-size: 4rem;
color: #667eea;
margin-bottom: 1rem;
}
.empty-cards h3, .empty-history h3, .empty-credits h3 {
color: white;
font-size: 1.5rem;
margin-bottom: 0.5rem;
}
.empty-cards p, .empty-history p, .empty-credits p {
color: #a0aec0;
margin-bottom: 2rem;
}
.add-first-card-btn {
background: linear-gradient(135deg, #667eea, #764ba2);
color: white;
border: none;
padding: 1rem 2rem;
border-radius: 8px;
font-size: 1.1rem;
font-weight: 600;
cursor: pointer;
transition: all 0.3s ease;
display: inline-flex;
align-items: center;
gap: 0.5rem;
}
.cards-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
gap: 1.5rem;
}
.card-item {
background: rgba(255, 255, 255, 0.05);
border: 1px solid rgba(255, 255, 255, 0.1);
border-radius: 12px;
padding: 1.5rem;
transition: all 0.3s ease;
}
.card-item:hover {
border-color: rgba(102, 126, 234, 0.3);
transform: translateY(-2px);
}
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 1rem;
}
.card-brand {
display: flex;
align-items: center;
gap: 0.5rem;
color: #667eea;
font-weight: 600;
}
.card-brand i {
font-size: 1.5rem;
}
.default-badge {
background: #48bb78;
color: white;
padding: 0.3rem 0.8rem;
border-radius: 6px;
font-size: 0.8rem;
font-weight: 600;
}
.card-number {
font-size: 1.2rem;
color: white;
font-weight: 600;
margin-bottom: 0.5rem;
}
.card-details {
color: #a0aec0;
font-size: 0.9rem;
margin-bottom: 1rem;
}
.card-actions {
display: flex;
justify-content: flex-end;
}
.remove-card-btn {
background: rgba(220, 53, 69, 0.8);
color: white;
border: none;
padding: 0.5rem 1rem;
border-radius: 6px;
font-size: 0.9rem;
cursor: pointer;
transition: all 0.3s ease;
}
.remove-card-btn:hover {
background: rgba(220, 53, 69, 1);
}
.transactions-list, .credit-activity {
display: flex;
flex-direction: column;
gap: 1rem;
}
.transaction-item, .credit-item {
display: flex;
align-items: center;
gap: 1rem;
padding: 1.5rem;
background: rgba(255, 255, 255, 0.03);
border: 1px solid rgba(255, 255, 255, 0.05);
border-radius: 12px;
transition: all 0.3s ease;
}
.transaction-item:hover, .credit-item:hover {
background: rgba(255, 255, 255, 0.05);
}
.transaction-icon, .credit-icon {
width: 50px;
height: 50px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 1.2rem;
color: white;
}
.transaction-icon.credit-purchase {
background: linear-gradient(135deg, #48bb78, #38a169);
}
.transaction-icon.track-purchase {
background: linear-gradient(135deg, #667eea, #764ba2);
}
.credit-icon {
background: linear-gradient(135deg, #ed8936, #dd6b20);
}
.transaction-details, .credit-details {
flex: 1;
}
.transaction-title, .credit-action {
color: white;
font-weight: 600;
margin-bottom: 0.3rem;
}
.transaction-subtitle, .credit-track {
color: #a0aec0;
font-size: 0.9rem;
margin-bottom: 0.2rem;
}
.transaction-date, .credit-date {
color: #718096;
font-size: 0.8rem;
}
.transaction-amount, .credit-amount {
text-align: right;
}
.amount {
color: white;
font-weight: 600;
font-size: 1.1rem;
}
.credits {
color: #a0aec0;
font-size: 0.9rem;
}
.credit-amount {
color: #f56565;
font-weight: 600;
}
/* Modal Styles */
.modal-overlay {
display: none;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.8);
z-index: 9999;
align-items: center;
justify-content: center;
}
.modal-content {
background: #1a202c;
border-radius: 16px;
padding: 2rem;
max-width: 500px;
width: 90%;
border: 1px solid rgba(255, 255, 255, 0.1);
position: relative;
z-index: 10000;
}
.modal-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 2rem;
}
.modal-header h3 {
color: white;
margin: 0;
display: flex;
align-items: center;
gap: 0.8rem;
}
.close-modal {
background: none;
border: none;
color: #a0aec0;
font-size: 1.5rem;
cursor: pointer;
padding: 0.5rem;
}
.card-form {
display: flex;
flex-direction: column;
gap: 1.5rem;
}
.form-group {
display: flex;
flex-direction: column;
gap: 0.5rem;
}
.form-group label {
color: white;
font-weight: 600;
}
.stripe-element {
background: rgba(255, 255, 255, 0.05);
border: 1px solid rgba(255, 255, 255, 0.1);
border-radius: 8px;
padding: 1rem;
min-height: 50px;
}
.error-message {
color: #f56565;
font-size: 0.9rem;
min-height: 1.2rem;
}
.form-actions {
display: flex;
gap: 1rem;
}
.cancel-btn {
flex: 1;
padding: 1rem;
background: rgba(255, 255, 255, 0.1);
border: 1px solid rgba(255, 255, 255, 0.2);
color: #a0aec0;
border-radius: 8px;
cursor: pointer;
font-weight: 600;
}
.save-btn {
flex: 1;
padding: 1rem;
background: linear-gradient(135deg, #667eea, #764ba2);
color: white;
border: none;
border-radius: 8px;
font-weight: 600;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
gap: 0.5rem;
}
</style>
<script src="https://js.stripe.com/v3/"></script>
<script>
// Check if Stripe is loaded
if (typeof Stripe === 'undefined') {
console.error('❌ Stripe library not loaded!');
alert('Error: Stripe payment library failed to load. Please refresh the page.');
} else {
console.log('✅ Stripe library loaded successfully');
}
const stripe = Stripe('pk_live_51Rn8TtD0zXLMB4gHMCZ5OMunyo0YtN6hBR30BoXFEiQxPG9I6U2tko6Axxwl0yJS21DCCykhC9PxAMdZoEfwJI0p00KlrZUR3w');
let elements, card;
let formListenerAttached = false;
function showAddCardModal() {
console.log('🎯 showAddCardModal called');
console.log('🎯 Stripe object:', stripe);
console.log('🎯 Modal element:', document.getElementById('addCardModal'));
const modal = document.getElementById('addCardModal');
if (!modal) {
console.error('❌ addCardModal not found!');
alert('Error: Payment modal not found');
return;
}
modal.style.display = 'flex';
console.log('🎯 Modal displayed');
// Initialize Stripe Elements if not already done
if (!elements) {
try {
console.log('🎯 Initializing Stripe Elements...');
elements = stripe.elements();
card = elements.create('card', {
style: {
base: {
color: '#ffffff',
fontFamily: '"Helvetica Neue", Helvetica, sans-serif',
fontSize: '16px',
'::placeholder': {
color: '#a0aec0'
},
iconColor: '#667eea'
},
invalid: {
color: '#f56565',
iconColor: '#f56565'
}
}
});
const cardElement = document.getElementById('card-element');
if (!cardElement) {
console.error('❌ card-element not found!');
alert('Error: Card input field not found');
return;
}
console.log('🎯 Mounting card element...');
card.mount('#card-element');
card.addEventListener('change', function(event) {
const displayError = document.getElementById('card-errors');
if (event.error) {
displayError.textContent = event.error.message;
} else {
displayError.textContent = '';
}
});
console.log('✅ Stripe Elements initialized successfully');
} catch (error) {
console.error('❌ Error initializing Stripe Elements:', error);
alert('Error initializing payment form: ' + error.message);
}
} else {
console.log('🎯 Stripe Elements already initialized');
}
}
function closeAddCardModal() {
document.getElementById('addCardModal').style.display = 'none';
}
// Only attach the event listener once
if (!formListenerAttached) {
document.getElementById('addCardForm').addEventListener('submit', async function(e) {
formListenerAttached = true;
e.preventDefault();
console.log('🎯 Form submission started');
const submitButton = this.querySelector('button[type="submit"]');
// Prevent multiple submissions
if (submitButton.disabled) {
console.log('🎯 Form already being processed, ignoring duplicate submission');
return;
}
const originalText = submitButton.innerHTML;
submitButton.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Processing...';
submitButton.disabled = true;
try {
console.log('🎯 Creating payment method...');
const { paymentMethod, error } = await stripe.createPaymentMethod({
type: 'card',
card: card,
});
if (error) {
console.error('❌ Stripe error:', error);
alert('Error: ' + error.message);
return;
}
console.log('🎯 Payment method created:', paymentMethod.id);
console.log('🎯 Sending to API...');
const response = await fetch('api/add_payment_method.php', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ payment_method_id: paymentMethod.id })
});
console.log('🎯 API response status:', response.status);
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
const responseText = await response.text();
console.log('🎯 API response text:', responseText);
let data;
try {
data = JSON.parse(responseText);
} catch (parseError) {
console.error('❌ JSON parse error:', parseError);
console.error('❌ Response text:', responseText);
throw new Error('Invalid response from server');
}
if (data.success) {
console.log('✅ Card added successfully');
alert('Card added successfully!');
// Close the modal
closeAddCardModal();
// Check site settings for refresh method
<?php if (isAutoRefreshPaymentMethods()): ?>
// Use AJAX refresh if enabled
refreshPaymentMethods();
<?php else: ?>
// Use page reload if AJAX refresh is disabled
location.reload();
<?php endif; ?>
} else {
console.error('❌ API error:', data.error);
alert('Error: ' + (data.error || 'Failed to add card'));
}
} catch (error) {
console.error('❌ Form submission error:', error);
alert('Error: ' + error.message);
} finally {
submitButton.innerHTML = originalText;
submitButton.disabled = false;
}
});
}
async function refreshPaymentMethods() {
console.log('🎯 Refreshing payment methods...');
try {
const response = await fetch('api/get_payment_methods.php');
console.log('🎯 API response status:', response.status);
const responseText = await response.text();
console.log('🎯 API response text:', responseText);
let data;
try {
data = JSON.parse(responseText);
} catch (parseError) {
console.error('❌ JSON parse error:', parseError);
console.error('❌ Response text:', responseText);
return;
}
if (data.success) {
console.log('✅ Payment methods refreshed:', data.payment_methods);
// Update the cards container
const cardsContainer = document.querySelector('.cards-container');
if (cardsContainer) {
if (data.payment_methods.length === 0) {
cardsContainer.innerHTML = `
<div class="empty-cards">
<div class="empty-icon">
<i class="fas fa-credit-card"></i>
</div>
<h3><?= t('account.no_cards') ?></h3>
<p><?= t('account.add_first_card') ?></p>
<button class="add-first-card-btn" onclick="showAddCardModal()">
<i class="fas fa-plus"></i> <?= t('account.add_first_card') ?>
</button>
</div>
`;
} else {
let cardsHTML = '<div class="cards-grid">';
data.payment_methods.forEach(pm => {
cardsHTML += `
<div class="card-item">
<div class="card-header">
<div class="card-brand">
<i class="fab fa-cc-${pm.card.brand}"></i>
<span>${pm.card.brand.charAt(0).toUpperCase() + pm.card.brand.slice(1)}</span>
</div>
${pm.id === (data.default_payment_method_id || null) ? '<div class="default-badge"><?= t('account.default') ?></div>' : ''}
</div>
<div class="card-number">
•••• •••• •••• ${pm.card.last4}
</div>
<div class="card-details">
<span><?= t('account.expires') ?> ${pm.card.exp_month}/${pm.card.exp_year}</span>
</div>
<div class="card-actions">
<button class="remove-card-btn" onclick="removeCard('${pm.id}')">
<i class="fas fa-trash"></i> <?= t('account.remove') ?>
</button>
</div>
</div>
`;
});
cardsHTML += '</div>';
cardsContainer.innerHTML = cardsHTML;
}
}
} else {
console.error('❌ Failed to refresh payment methods:', data.error);
}
} catch (error) {
console.error('❌ Error refreshing payment methods:', error);
}
}
// Add direct event listeners to ensure buttons work
document.addEventListener('DOMContentLoaded', function() {
console.log('🎯 DOM loaded, setting up button listeners');
// Add listener to all add card buttons
const addCardButtons = document.querySelectorAll('.add-card-btn, .add-first-card-btn');
addCardButtons.forEach(button => {
console.log('🎯 Adding listener to button:', button);
button.addEventListener('click', function(e) {
e.preventDefault();
console.log('🎯 Button clicked via event listener');
showAddCardModal();
});
});
});
function removeCard(paymentMethodId) {
if (!confirm('Are you sure you want to remove this card?')) {
return;
}
fetch('api/remove_payment_method.php', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ payment_method_id: paymentMethodId })
})
.then(res => res.json())
.then(data => {
if (data.success) {
alert('Card removed successfully!');
location.reload();
} else {
alert('Error: ' + (data.error || 'Failed to remove card'));
}
})
.catch(error => {
alert('Error: ' + error.message);
});
}
</script>
<?php elseif ($current_tab === 'privacy'): ?>
<!-- Privacy Tab -->
<div class="settings-section">
<h3><i class="fas fa-user-secret"></i> Privacy Settings</h3>
<p>Control how your information is shared and displayed.</p>
<div class="privacy-options">
<div class="privacy-option">
<div class="privacy-info">
<h4><?= t('account.profile_visibility') ?></h4>
<p><?= t('account.control_profile_visibility') ?? 'Contrôlez qui peut voir votre profil et vos morceaux' ?></p>
</div>
<select class="privacy-select" onchange="updatePrivacy('profile_visibility', this.value)">
<option value="public"><?= t('account.profile_visibility_public') ?></option>
<option value="followers"><?= t('account.followers_only') ?? 'Abonnés uniquement' ?></option>
<option value="private"><?= t('account.profile_visibility_private') ?></option>
</select>
</div>
<div class="privacy-option">
<div class="privacy-info">
<h4><?= t('account.track_visibility') ?? 'Visibilité des morceaux' ?></h4>
<p><?= t('account.control_track_visibility') ?? 'Contrôlez qui peut voir et acheter vos morceaux' ?></p>
</div>
<select class="privacy-select" onchange="updatePrivacy('track_visibility', this.value)">
<option value="public"><?= t('account.profile_visibility_public') ?></option>
<option value="followers"><?= t('account.followers_only') ?? 'Abonnés uniquement' ?></option>
<option value="private"><?= t('account.profile_visibility_private') ?></option>
</select>
</div>
<div class="privacy-option">
<div class="privacy-info">
<h4><?= t('account.activity_status') ?></h4>
<p><?= t('account.show_online') ?></p>
</div>
<label class="toggle-switch">
<input type="checkbox" checked onchange="updatePrivacy('activity_status', this.checked)">
<span class="toggle-slider"></span>
</label>
</div>
<div class="privacy-option">
<div class="privacy-info">
<h4><?= t('account.purchase_history') ?></h4>
<p><?= t('account.show_purchases') ?></p>
</div>
<label class="toggle-switch">
<input type="checkbox" onchange="updatePrivacy('purchase_history', this.checked)">
<span class="toggle-slider"></span>
</label>
</div>
<div class="privacy-option">
<div class="privacy-info">
<h4><?= t('account.data_analytics') ?></h4>
<p><?= t('account.help_improve') ?></p>
</div>
<label class="toggle-switch">
<input type="checkbox" checked onchange="updatePrivacy('data_analytics', this.checked)">
<span class="toggle-slider"></span>
</label>
</div>
</div>
</div>
<div class="settings-section">
<h3><i class="fas fa-download"></i> <?= t('account.data_export') ?></h3>
<p><?= t('account.download_data') ?></p>
<div class="data-actions">
<button class="export-btn" onclick="exportUserData()">
<i class="fas fa-download"></i>
<?= t('account.export_my_data') ?>
</button>
<button class="delete-data-btn" onclick="requestDataDeletion()">
<i class="fas fa-trash"></i>
<?= t('account.request_data_deletion') ?>
</button>
</div>
</div>
<?php elseif ($current_tab === 'notifications'): ?>
<!-- Notifications Tab -->
<div class="settings-section">
<h3><i class="fas fa-bell"></i> <?= t('account.email_notifications') ?></h3>
<p><?= t('account.choose_emails') ?></p>
<div class="notification-options">
<div class="notification-option">
<div class="notification-info">
<h4><?= t('account.purchase_confirmations') ?></h4>
<p><?= t('account.notified_purchases') ?></p>
</div>
<label class="toggle-switch">
<input type="checkbox" checked onchange="updateNotification('purchase_confirmations', this.checked)">
<span class="toggle-slider"></span>
</label>
</div>
<div class="notification-option">
<div class="notification-info">
<h4><?= t('account.new_track_releases') ?></h4>
<p><?= t('account.notified_new_tracks') ?></p>
</div>
<label class="toggle-switch">
<input type="checkbox" checked onchange="updateNotification('new_tracks', this.checked)">
<span class="toggle-slider"></span>
</label>
</div>
<div class="notification-option">
<div class="notification-info">
<h4><?= t('account.credit_alerts') ?></h4>
<p><?= t('account.notified_low_credits') ?></p>
</div>
<label class="toggle-switch">
<input type="checkbox" checked onchange="updateNotification('credit_alerts', this.checked)">
<span class="toggle-slider"></span>
</label>
</div>
<div class="notification-option">
<div class="notification-info">
<h4><?= t('account.weekly_newsletter') ?></h4>
<p><?= t('account.receive_updates') ?></p>
</div>
<label class="toggle-switch">
<input type="checkbox" onchange="updateNotification('newsletter', this.checked)">
<span class="toggle-slider"></span>
</label>
</div>
<div class="notification-option">
<div class="notification-info">
<h4><?= t('account.account_security') ?></h4>
<p><?= t('account.notified_security') ?></p>
</div>
<label class="toggle-switch">
<input type="checkbox" checked onchange="updateNotification('security', this.checked)">
<span class="toggle-slider"></span>
</label>
</div>
</div>
</div>
<div class="settings-section">
<h3><i class="fas fa-mobile-alt"></i> <?= t('account.push_notifications') ?></h3>
<p><?= t('account.control_push') ?></p>
<div class="notification-options">
<div class="notification-option">
<div class="notification-info">
<h4><?= t('account.enable_push') ?></h4>
<p><?= t('account.receive_when_closed') ?></p>
</div>
<label class="toggle-switch">
<input type="checkbox" onchange="togglePushNotifications(this.checked)">
<span class="toggle-slider"></span>
</label>
</div>
</div>
</div>
<?php elseif ($current_tab === 'connections'): ?>
<!-- Connections Tab -->
<div class="settings-section">
<h3><i class="fas fa-link"></i> <?= t('account.social_media_connections') ?></h3>
<p><?= t('account.connect_social_desc') ?></p>
<div class="social-connections">
<div class="social-connection-card">
<div class="social-info">
<div class="social-icon">
<i class="fab fa-google"></i>
</div>
<div class="social-details">
<div class="social-name">Google</div>
<div class="social-email">user@gmail.com</div>
</div>
<div class="connection-status connected">Connected</div>
</div>
<div class="social-actions">
<button class="disconnect-btn" onclick="disconnectSocial('google')">
<i class="fas fa-unlink"></i>
Disconnect
</button>
</div>
</div>
<div class="social-connection-card">
<div class="social-info">
<div class="social-icon">
<i class="fab fa-facebook"></i>
</div>
<div class="social-details">
<div class="social-name">Facebook</div>
<div class="social-email">Not connected</div>
</div>
<div class="connection-status disconnected">Not Connected</div>
</div>
<div class="social-actions">
<button class="connect-btn" onclick="connectSocial('facebook')">
<i class="fas fa-link"></i>
Connect
</button>
</div>
</div>
<div class="social-connection-card">
<div class="social-info">
<div class="social-icon">
<i class="fab fa-twitter"></i>
</div>
<div class="social-details">
<div class="social-name">Twitter</div>
<div class="social-email">Not connected</div>
</div>
<div class="connection-status disconnected">Not Connected</div>
</div>
<div class="social-actions">
<button class="connect-btn" onclick="connectSocial('twitter')">
<i class="fas fa-link"></i>
Connect
</button>
</div>
</div>
<div class="social-connection-card">
<div class="social-info">
<div class="social-icon">
<i class="fab fa-apple"></i>
</div>
<div class="social-details">
<div class="social-name">Apple</div>
<div class="social-email">Not connected</div>
</div>
<div class="connection-status disconnected">Not Connected</div>
</div>
<div class="social-actions">
<button class="connect-btn" onclick="connectSocial('apple')">
<i class="fas fa-link"></i>
Connect
</button>
</div>
</div>
<div class="social-connection-card">
<div class="social-info">
<div class="social-icon">
<i class="fab fa-discord"></i>
</div>
<div class="social-details">
<div class="social-name">Discord</div>
<div class="social-email">Not connected</div>
</div>
<div class="connection-status disconnected">Not Connected</div>
</div>
<div class="social-actions">
<button class="connect-btn" onclick="connectSocial('discord')">
<i class="fas fa-link"></i>
Connect
</button>
</div>
</div>
<div class="social-connection-card">
<div class="social-info">
<div class="social-icon">
<i class="fab fa-github"></i>
</div>
<div class="social-details">
<div class="social-name">GitHub</div>
<div class="social-email">Not connected</div>
</div>
<div class="connection-status disconnected">Not Connected</div>
</div>
<div class="social-actions">
<button class="connect-btn" onclick="connectSocial('github')">
<i class="fas fa-link"></i>
Connect
</button>
</div>
</div>
</div>
</div>
<div class="settings-section">
<h3><i class="fas fa-share-alt"></i> Sharing Preferences</h3>
<p>Control how your music is shared on social platforms.</p>
<div class="sharing-options">
<div class="sharing-option">
<div class="sharing-info">
<h4>Auto-share New Tracks</h4>
<p>Automatically share new tracks to connected social accounts</p>
</div>
<label class="toggle-switch">
<input type="checkbox" onchange="updateSharing('auto_share', this.checked)">
<span class="toggle-slider"></span>
</label>
</div>
<div class="sharing-option">
<div class="sharing-info">
<h4>Share Purchase Activity</h4>
<p>Share when you purchase tracks from other artists</p>
</div>
<label class="toggle-switch">
<input type="checkbox" onchange="updateSharing('share_purchases', this.checked)">
<span class="toggle-slider"></span>
</label>
</div>
<div class="sharing-option">
<div class="sharing-info">
<h4>Cross-platform Sync</h4>
<p>Sync your profile information across connected platforms</p>
</div>
<label class="toggle-switch">
<input type="checkbox" checked onchange="updateSharing('cross_platform_sync', this.checked)">
<span class="toggle-slider"></span>
</label>
</div>
</div>
</div>
<?php elseif ($current_tab === 'settings'): ?>
<!-- Settings Tab -->
<div class="settings-section">
<h3><i class="fas fa-user"></i> <?= t('account.account_information') ?></h3>
<p><?= t('account.update_details') ?></p>
<form id="accountForm">
<?php
// Extract values to ensure they're properly set
// Use database values first, fallback to session if empty
$email_value = !empty($user['email']) ? trim($user['email']) : (isset($_SESSION['user_email']) ? trim($_SESSION['user_email']) : '');
$name_value = !empty($user['name']) ? trim($user['name']) : (isset($_SESSION['user_name']) ? trim($_SESSION['user_name']) : '');
// Handle custom_url - check if it exists and is not empty/null
// COALESCE returns empty string if NULL, but we need to check the actual value
$custom_url_value = '';
if (isset($user['custom_url'])) {
$custom_url_raw = $user['custom_url'];
// Handle both NULL and empty string cases
if ($custom_url_raw !== null && $custom_url_raw !== '' && trim($custom_url_raw) !== '') {
$custom_url_value = trim($custom_url_raw);
}
}
// Debug custom_url specifically
error_log("Custom URL Debug - Raw value: " . var_export($user['custom_url'] ?? 'NOT SET', true));
error_log("Custom URL Debug - Final value: " . var_export($custom_url_value, true));
// Additional debug - verify data exists
if (empty($email_value) && empty($name_value)) {
error_log("WARNING: User data appears empty for user ID: " . $_SESSION['user_id']);
error_log("User array keys: " . implode(', ', array_keys($user ?? [])));
error_log("User data dump: " . print_r($user, true));
error_log("Session user_name: " . ($_SESSION['user_name'] ?? 'NOT SET'));
error_log("Session user_email: " . ($_SESSION['user_email'] ?? 'NOT SET'));
}
?>
<div class="form-group">
<label class="form-label"><?= t('account.email_address') ?></label>
<input type="email" class="form-input" name="email" id="account_email" value="<?= htmlspecialchars($email_value, ENT_QUOTES, 'UTF-8') ?>" required>
</div>
<div class="form-group">
<label class="form-label"><?= t('account.display_name') ?></label>
<input type="text" class="form-input" name="name" id="account_name" value="<?= htmlspecialchars($name_value, ENT_QUOTES, 'UTF-8') ?>" required>
</div>
<div class="form-group">
<label class="form-label"><?= t('account.custom_profile_url') ?></label>
<div class="url-input-group">
<span class="url-prefix">soundstudiopro.com/</span>
<input type="text" class="form-input url-input" name="custom_url" id="account_custom_url"
value="<?= htmlspecialchars($custom_url_value, ENT_QUOTES, 'UTF-8') ?>"
placeholder="your-custom-url"
pattern="[a-zA-Z0-9-]+"
title="Only letters, numbers, and hyphens allowed"
style="text-transform: lowercase;">
</div>
<small class="form-help"><?= t('account.custom_url_help') ?></small>
<?php if (!empty($custom_url_value)): ?>
<small class="form-help" style="color: #48bb78; display: block; margin-top: 0.5rem;">
<i class="fas fa-check-circle"></i> Your profile URL: <a href="/<?= htmlspecialchars($custom_url_value, ENT_QUOTES, 'UTF-8') ?>" target="_blank" style="color: #667eea;">soundstudiopro.com/<?= htmlspecialchars($custom_url_value, ENT_QUOTES, 'UTF-8') ?></a>
</small>
<?php endif; ?>
</div>
<button type="submit" class="save-btn">
<i class="fas fa-save"></i>
<?= t('account.save_changes') ?>
</button>
</form>
<script>
// Debug: Log form field values on page load to verify they're populated
(function() {
setTimeout(function() {
const emailField = document.getElementById('account_email');
const nameField = document.getElementById('account_name');
const customUrlField = document.getElementById('account_custom_url');
console.log('=== Account Settings Debug ===');
if (emailField) {
console.log('Email field value:', emailField.value || '(empty)');
} else {
console.log('Email field not found!');
}
if (nameField) {
console.log('Name field value:', nameField.value || '(empty)');
} else {
console.log('Name field not found!');
}
if (customUrlField) {
console.log('Custom URL field value:', customUrlField.value || '(empty)');
} else {
console.log('Custom URL field not found!');
}
}, 100);
})();
</script>
</div>
<div class="settings-section">
<h3><i class="fas fa-bell"></i> <?= t('account.notifications') ?></h3>
<p><?= t('account.manage_notifications') ?></p>
<div class="form-group">
<label class="form-label">
<input type="checkbox" checked> <?= t('account.email_new_tracks') ?>
</label>
</div>
<div class="form-group">
<label class="form-label">
<input type="checkbox" checked> <?= t('account.credit_purchase_confirmations') ?>
</label>
</div>
<div class="form-group">
<label class="form-label">
<input type="checkbox"> <?= t('account.weekly_recommendations') ?>
</label>
</div>
</div>
<?php elseif ($current_tab === 'security'): ?>
<!-- Security Tab -->
<div class="settings-section">
<h3><i class="fas fa-lock"></i> <?= t('account.change_password') ?></h3>
<p><?= t('account.update_password') ?></p>
<form id="passwordForm">
<div class="form-group">
<label class="form-label"><?= t('account.current_password') ?></label>
<input type="password" class="form-input" name="current_password" required>
</div>
<div class="form-group">
<label class="form-label"><?= t('account.new_password') ?></label>
<input type="password" class="form-input" name="new_password" required>
</div>
<div class="form-group">
<label class="form-label"><?= t('account.confirm_new_password') ?></label>
<input type="password" class="form-input" name="confirm_password" required>
</div>
<button type="submit" class="save-btn">
<i class="fas fa-key"></i>
<?= t('account.update_password_btn') ?>
</button>
</form>
</div>
<div class="settings-section">
<h3><i class="fas fa-trash"></i> <?= t('account.account_deletion') ?></h3>
<p><?= t('account.delete_account_desc') ?></p>
<button class="save-btn" style="background: linear-gradient(135deg, #dc3545, #c82333);" onclick="confirmAccountDeletion()">
<i class="fas fa-trash"></i>
<?= t('account.delete_account') ?>
</button>
</div>
<?php endif; ?>
</div>
</div>
<script>
function playTrack(audioUrl, title) {
if (typeof window.playTrackWithGlobalPlayer === 'function') {
window.playTrackWithGlobalPlayer(audioUrl, title, 'Account Track');
} else {
// Fallback
const audio = new Audio(audioUrl);
audio.play();
}
}
// Sanitize filename (same as library.php)
function sanitizeFilename(str) {
if (!str) return '';
return str.replace(/[<>:"/\\|?*]/g, '').replace(/\s+/g, ' ').trim();
}
// Generate filename with metadata (same format as library.php)
function generateDownloadFilename(trackData, type = 'original') {
const cleanTitle = sanitizeFilename(trackData.title || 'Untitled Track');
const cleanArtist = sanitizeFilename(trackData.artist || 'Unknown Artist');
const cleanGenre = sanitizeFilename(trackData.genre || '');
const cleanKey = sanitizeFilename(trackData.key || '');
const cleanMood = sanitizeFilename(trackData.mood || '');
const cleanNumericalKey = sanitizeFilename(trackData.numericalKey || '');
const bpm = trackData.bpm || '';
// Build filename: Artist - Title [or (Variation X) for variations]
let filename = `${cleanArtist} - ${cleanTitle}`;
if (type === 'variation') {
const variationNum = (trackData.variation_index ?? 0) + 1;
const totalVariations = trackData.total_variations || 1;
if (totalVariations > 1) {
filename += ` (Variation ${variationNum} of ${totalVariations})`;
} else {
filename += ` (Variation ${variationNum})`;
}
}
// Add metadata to filename
const metadataParts = [];
if (cleanGenre && cleanGenre !== 'Unknown') metadataParts.push(cleanGenre);
if (bpm) metadataParts.push(`${bpm} BPM`);
// Add numerical key first if available, then the musical key
if (cleanNumericalKey) metadataParts.push(cleanNumericalKey);
if (cleanKey) metadataParts.push(cleanKey);
if (cleanMood) metadataParts.push(cleanMood);
if (metadataParts.length > 0) {
filename += ` [${metadataParts.join(', ')}]`;
}
filename += '.mp3';
return filename;
}
// Download track with proper filename metadata
function downloadTrackWithMetadata(trackData, type = 'original') {
const filename = generateDownloadFilename(trackData, type);
const audioUrl = trackData.audio_url;
// Create download link
const link = document.createElement('a');
link.href = audioUrl;
link.download = filename;
link.target = '_blank';
// Add to DOM, click, and remove
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
// Record download in database
const variationId = type === 'variation' ? trackData.variation_id : null;
recordDownload(trackData.purchase_id, type, variationId);
}
function recordDownload(purchaseId, type = 'original', variationId = null) {
// Record download in database
fetch('/api/record_download.php', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
purchase_id: purchaseId,
download_type: type,
variation_id: variationId
})
}).catch(error => {
console.error('Error recording download:', error);
});
}
function confirmAccountDeletion() {
if (confirm('Are you sure you want to permanently delete your account? This action cannot be undone and will remove all your data, purchases, and tracks.')) {
if (confirm('This is your final warning. All your data will be permanently lost. Are you absolutely sure?')) {
// Handle account deletion
alert('Account deletion feature will be implemented soon.');
}
}
}
// Handle account form submission
const accountForm = document.getElementById('accountForm');
if (accountForm) {
accountForm.addEventListener('submit', async (e) => {
e.preventDefault();
const formData = new FormData(e.target);
const data = {};
// Manually extract form data
for (let [key, value] of formData.entries()) {
// Trim all values except preserve custom_url exactly as typed (will be lowercased on server)
if (key === 'custom_url') {
data[key] = value.trim();
} else {
data[key] = value.trim();
}
}
// Ensure custom_url is always included, even if empty
if (!('custom_url' in data)) {
data.custom_url = '';
}
// Debug: Log what we're sending
console.log('Sending account update data:', data);
console.log('Custom URL value being sent:', data.custom_url);
try {
const response = await fetch('/api/update_account.php', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(data)
});
const responseData = await response.json();
console.log('Account update response:', responseData);
console.log('Response status:', response.status);
if (response.ok) {
// Show success message with more detail
const message = responseData.message || 'Account updated successfully!';
alert(message);
// Force full page reload with cache bypass to ensure fresh data
window.location.href = window.location.pathname + '?tab=settings&_refresh=' + Date.now();
} else {
// Show detailed error message
const errorMsg = responseData.error || 'Please try again.';
console.error('Account update failed:', errorMsg);
alert('Error updating account: ' + errorMsg);
}
} catch (error) {
console.error('Account update error:', error);
alert('Error updating account: ' + error.message + '. Please check the console for details.');
}
});
}
// Handle password form submission
document.getElementById('passwordForm').addEventListener('submit', async (e) => {
e.preventDefault();
const formData = new FormData(e.target);
const data = Object.fromEntries(formData);
if (data.new_password !== data.confirm_password) {
alert('New passwords do not match!');
return;
}
try {
const response = await fetch('/api/update_password.php', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(data)
});
if (response.ok) {
alert('Password updated successfully!');
e.target.reset();
} else {
alert('Error updating password. Please check your current password and try again.');
}
} catch (error) {
alert('Error updating password. Please try again.');
}
});
// Subscription functions
function upgradePlan() {
if(window.ajaxNavigation){window.ajaxNavigation.navigateToPage('/credits.php')}else{window.location.href='/credits.php';}
}
function cancelSubscription() {
if (confirm('Are you sure you want to cancel your subscription? You will lose access to premium features at the end of your current billing period.')) {
alert('Subscription cancellation feature will be implemented soon.');
}
}
// Payment method functions
function addPaymentMethod() {
alert('Payment method addition will be implemented soon.');
}
function editPaymentMethod(id) {
alert('Payment method editing will be implemented soon.');
}
function removePaymentMethod(id) {
if (confirm('Are you sure you want to remove this payment method?')) {
alert('Payment method removal will be implemented soon.');
}
}
// Privacy functions
function updatePrivacy(setting, value) {
console.log(`Privacy setting ${setting} updated to ${value}`);
// API call would go here
showNotification(`Privacy setting updated: ${setting}`, 'success');
}
function exportUserData() {
alert('Data export feature will be implemented soon. This will generate a downloadable file with all your account data.');
}
function requestDataDeletion() {
if (confirm('Are you sure you want to request data deletion? This will permanently remove all your data from our servers.')) {
if (confirm('This action cannot be undone. Are you absolutely sure?')) {
alert('Data deletion request will be processed within 30 days as per GDPR requirements.');
}
}
}
// Notification functions
function updateNotification(setting, value) {
console.log(`Notification setting ${setting} updated to ${value}`);
// API call would go here
showNotification(`Notification setting updated: ${setting}`, 'success');
}
function togglePushNotifications(enabled) {
if (enabled) {
if ('Notification' in window) {
Notification.requestPermission().then(permission => {
if (permission === 'granted') {
showNotification('Push notifications enabled!', 'success');
} else {
showNotification('Push notifications permission denied', 'error');
}
});
} else {
showNotification('Push notifications not supported in this browser', 'error');
}
} else {
showNotification('Push notifications disabled', 'success');
}
}
// Billing form submission
document.getElementById('billingForm').addEventListener('submit', async (e) => {
e.preventDefault();
const formData = new FormData(e.target);
const data = Object.fromEntries(formData);
try {
const response = await fetch('/api/update_billing.php', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(data)
});
if (response.ok) {
showNotification('Billing address updated successfully!', 'success');
} else {
showNotification('Error updating billing address. Please try again.', 'error');
}
} catch (error) {
showNotification('Error updating billing address. Please try again.', 'error');
}
});
// Enhanced notification function
function showNotification(message, type = 'info') {
const notification = document.createElement('div');
notification.style.cssText = `
position: fixed;
top: 100px;
right: 20px;
background: ${type === 'success' ? '#48bb78' : type === 'error' ? '#f56565' : 'var(--bg-card)'};
color: white;
padding: 1.5rem 2rem;
border-radius: 12px;
box-shadow: var(--shadow-medium);
border: 2px solid ${type === 'success' ? '#38a169' : type === 'error' ? '#e53e3e' : 'var(--border-medium)'};
z-index: 10000;
font-size: 1.4rem;
font-weight: 500;
transform: translateX(100%);
transition: transform 0.3s ease;
max-width: 300px;
`;
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 (document.body.contains(notification)) {
document.body.removeChild(notification);
}
}, 300);
}, 3000);
}
// Social connection functions
function connectSocial(provider) {
// Redirect to OAuth flow
const authUrl = `/auth/${provider}_auth.php`;
window.open(authUrl, '_blank', 'width=500,height=600');
// Listen for auth completion
window.addEventListener('message', function(event) {
if (event.data.type === 'social_auth_complete') {
showNotification(`${provider} connected successfully!`, 'success');
location.reload();
}
});
}
function disconnectSocial(provider) {
if (confirm(`Are you sure you want to disconnect your ${provider} account?`)) {
fetch('/api/disconnect_social.php', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ provider: provider })
})
.then(response => response.json())
.then(data => {
if (data.success) {
showNotification(`${provider} disconnected successfully!`, 'success');
location.reload();
} else {
showNotification(`Error disconnecting ${provider}: ${data.error}`, 'error');
}
})
.catch(error => {
showNotification(`Error disconnecting ${provider}`, 'error');
});
}
}
function updateSharing(setting, value) {
fetch('/api/update_sharing.php', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ setting: setting, value: value })
})
.then(response => response.json())
.then(data => {
if (data.success) {
showNotification('Sharing preference updated!', 'success');
} else {
showNotification('Error updating sharing preference', 'error');
}
})
.catch(error => {
showNotification('Error updating sharing preference', 'error');
});
}
</script>
<?php if ($current_tab === 'tickets'): ?>
<script>
function copyTicketCode(code, el) {
if (!navigator.clipboard) {
alert('<?= addslashes(t('tickets.copy_code')) ?>');
return;
}
const original = el.innerHTML;
navigator.clipboard.writeText(code).then(() => {
el.innerHTML = '<i class="fas fa-check"></i><?= addslashes(t('tickets.code_copied')) ?>';
setTimeout(() => {
el.innerHTML = original;
}, 2000);
}).catch(() => {
alert('<?= addslashes(t('tickets.copy_code')) ?>');
});
}
</script>
<?php endif; ?>
</main>
<?php include 'includes/footer.php'; ?>