PNG IHDR x sBIT|d pHYs + tEXtSoftware www.inkscape.org< ,tEXtComment
<?php
session_start();
// 1. Ensure user is logged in
if (!isset($_SESSION['user_id'])) {
header("Location: login.php");
exit();
}
$host = 'localhost';
$dbname = 'u264723324_NuDb';
$user = 'u264723324_NuUu';
$pass = '@WdsdsdAq1231';
try {
$pdo = new PDO("mysql:host=$host;dbname=$dbname;charset=utf8mb4", $user, $pass);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$userId = $_SESSION['user_id'];
// ========================================================================
// AJAX HANDLER: SILENTLY UPDATE CARD SETTINGS & WALLPAPER
// ========================================================================
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['ajax_action'])) {
header('Content-Type: application/json');
$cardId = filter_input(INPUT_POST, 'card_id', FILTER_VALIDATE_INT);
if (!$cardId) {
echo json_encode(['success' => false, 'message' => 'Invalid card ID']);
exit;
}
// Verify the card belongs to this user
$checkStmt = $pdo->prepare("SELECT id, status FROM cards WHERE id = ? AND user_id = ?");
$checkStmt->execute([$cardId, $userId]);
$cardCheck = $checkStmt->fetch();
if (!$cardCheck) {
echo json_encode(['success' => false, 'message' => 'Unauthorized access']);
exit;
}
// TOGGLE SETTINGS (Freeze, ATM, Online)
if ($_POST['ajax_action'] === 'toggle_setting') {
$setting = $_POST['setting'];
$value = $_POST['value'] === '1' ? 1 : 0;
$allowedSettings = ['is_frozen', 'allow_online', 'allow_atm'];
if (in_array($setting, $allowedSettings)) {
$stmt = $pdo->prepare("UPDATE cards SET $setting = ? WHERE id = ?");
$stmt->execute([$value, $cardId]);
echo json_encode(['success' => true, 'message' => 'Card setting updated.']);
} else {
echo json_encode(['success' => false, 'message' => 'Invalid setting']);
}
exit;
}
// UPDATE SPENDING LIMIT
if ($_POST['ajax_action'] === 'update_limit') {
$newLimit = filter_input(INPUT_POST, 'new_limit', FILTER_VALIDATE_FLOAT);
if ($newLimit && $newLimit > 0) {
$stmt = $pdo->prepare("UPDATE cards SET spending_limit = ? WHERE id = ?");
$stmt->execute([$newLimit, $cardId]);
echo json_encode(['success' => true, 'message' => 'Limit successfully updated to $' . number_format($newLimit, 2)]);
} else {
echo json_encode(['success' => false, 'message' => 'Invalid limit amount.']);
}
exit;
}
// UPDATE WALLPAPER STYLE
if ($_POST['ajax_action'] === 'update_style') {
$newStyle = filter_input(INPUT_POST, 'new_style', FILTER_SANITIZE_STRING);
$validStyles = ['ocean', 'onyx', 'ruby', 'emerald', 'gold'];
if (in_array($newStyle, $validStyles)) {
$stmt = $pdo->prepare("UPDATE cards SET card_style = ? WHERE id = ?");
$stmt->execute([$newStyle, $cardId]);
echo json_encode(['success' => true, 'message' => 'Card design updated successfully.']);
} else {
echo json_encode(['success' => false, 'message' => 'Invalid design selected.']);
}
exit;
}
}
// ========================================================================
// STANDARD PAGE LOAD & CARD CREATION
// ========================================================================
$stmt = $pdo->prepare("SELECT first_name, last_name, kyc_status FROM users WHERE id = :id LIMIT 1");
$stmt->execute([':id' => $userId]);
$userRow = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$userRow) { session_destroy(); header("Location: login.php"); exit(); }
$kyc_status = $userRow['kyc_status'];
$fullName = $userRow['first_name'] . ' ' . $userRow['last_name'];
$actionSuccess = false;
$actionError = '';
$actionMessage = '';
// Handle Form Submission (Create Card)
if ($_SERVER["REQUEST_METHOD"] == "POST" && isset($_POST['create_card'])) {
if ($kyc_status !== 'verified') {
$actionError = "Your account is not verified. Cannot issue new cards.";
} else {
$stmtAcc = $pdo->prepare("SELECT id, balance FROM accounts WHERE user_id = :id LIMIT 1");
$stmtAcc->execute([':id' => $userId]);
$primaryAcc = $stmtAcc->fetch(PDO::FETCH_ASSOC);
$primaryBalance = $primaryAcc ? $primaryAcc['balance'] : 0;
$cardType = $_POST['card_type'];
$cardStyle = $_POST['card_style'];
// CARD SPECIFICATIONS
$cardSpecs = [
'mastercard' => ['issue' => 15.00, 'yearly' => 10.00, 'min_bal' => 50.00, 'name' => 'Mastercard Standard', 'limit' => 5000],
'visa' => ['issue' => 12.00, 'yearly' => 8.00, 'min_bal' => 30.00, 'name' => 'Visa Classic', 'limit' => 3000],
'amex' => ['issue' => 50.00, 'yearly' => 150.00,'min_bal' => 500.00, 'name' => 'Amex Gold', 'limit' => 25000],
'verve' => ['issue' => 5.00, 'yearly' => 2.00, 'min_bal' => 10.00, 'name' => 'Verve Local', 'limit' => 1000]
];
if (array_key_exists($cardType, $cardSpecs)) {
$spec = $cardSpecs[$cardType];
if ($primaryBalance < $spec['min_bal']) {
$actionError = "Card requires a minimum account balance of $" . number_format($spec['min_bal'], 2);
} elseif ($primaryBalance < $spec['issue']) {
$actionError = "Insufficient balance for the $" . number_format($spec['issue'], 2) . " issuance fee.";
} else {
try {
$pdo->beginTransaction();
// Deduct Issuance Fee Only
$newBalance = $primaryBalance - $spec['issue'];
$pdo->prepare("UPDATE accounts SET balance = ? WHERE id = ?")->execute([$newBalance, $primaryAcc['id']]);
// Generate Realistic Card Details
$prefix = ($cardType === 'visa') ? '4' : '5';
if ($cardType === 'amex') $prefix = '3';
$cardNumber = $prefix . mt_rand(100, 999) . ' ' . mt_rand(1000, 9999) . ' ' . mt_rand(1000, 9999) . ' ' . mt_rand(1000, 9999);
$cvv = mt_rand(100, 999);
$expiry = str_pad(mt_rand(1, 12), 2, '0', STR_PAD_LEFT) . '/' . (date('y') + mt_rand(3, 5));
// Insert Card as PENDING
$stmtInsert = $pdo->prepare("INSERT INTO cards (user_id, card_type, card_style, card_number, cvv, expiry_date, spending_limit, status) VALUES (?, ?, ?, ?, ?, ?, ?, 'pending')");
$stmtInsert->execute([$userId, $spec['name'], $cardStyle, $cardNumber, $cvv, $expiry, $spec['limit']]);
// Record Transaction
$pdo->prepare("INSERT INTO transactions (account_id, transaction_type, amount, description, status) VALUES (?, 'debit', ?, ?, 'completed')")
->execute([$primaryAcc['id'], $spec['issue'], "Card Issuance Fee (" . $spec['name'] . ")"]);
$pdo->commit();
$actionSuccess = true;
$actionMessage = "Card requested! $" . number_format($spec['issue'], 2) . " fee deducted. Status is Pending.";
} catch (Exception $e) {
$pdo->rollBack();
$actionError = "Failed to generate card. Please try again.";
}
}
}
}
}
// Fetch all user cards to display in UI
$stmtCards = $pdo->prepare("SELECT * FROM cards WHERE user_id = ? ORDER BY created_at DESC");
$stmtCards->execute([$userId]);
$userCards = $stmtCards->fetchAll(PDO::FETCH_ASSOC);
// Convert PHP array to JSON so JavaScript can use it seamlessly
$cardsJson = json_encode($userCards);
} catch (PDOException $e) {
$kyc_status = 'pending';
$userCards = [];
$cardsJson = '[]';
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Cards - City Prime</title>
<style>
:root {
--bg-dark: #0a0e17; --surface-dark: #131a2a; --surface-light: #1e2738;
--accent-blue: #0ea5e9; --accent-yellow: #facc15; --text-main: #f8fafc;
--text-muted: #94a3b8; --danger: #ef4444; --success: #22c55e;
--card-gradient: linear-gradient(135deg, #0284c7, #0f172a);
}
* { margin: 0; padding: 0; box-sizing: border-box; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; -webkit-tap-highlight-color: transparent; }
body { background-color: var(--bg-dark); color: var(--text-main); }
.app-container { display: flex; flex-direction: column; min-height: 100vh; }
.sidebar { display: none; }
.main-content { flex: 1; padding: 20px; padding-bottom: 90px; overflow-x: hidden; }
.top-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 24px; }
.page-title { font-size: 1.5rem; font-weight: 700; }
.add-card-btn { font-size: 1.5rem; cursor: pointer; width: 40px; height: 40px; background: var(--surface-dark); border-radius: 50%; display: flex; align-items: center; justify-content: center; transition: 0.2s;}
.add-card-btn:active { transform: scale(0.9); background: var(--accent-blue);}
/* Carousel */
.card-carousel { display: flex; gap: 16px; overflow-x: auto; scrollbar-width: none; padding-bottom: 12px; margin-bottom: 24px; scroll-snap-type: x mandatory;}
.card-carousel::-webkit-scrollbar { display: none; }
.credit-card { min-width: 100%; scroll-snap-align: start; background: var(--card-gradient); border-radius: 20px; padding: 24px; position: relative; overflow: hidden; border: 1px solid rgba(255,255,255,0.1); box-shadow: 0 10px 30px rgba(0,0,0,0.5); transition: 0.3s;}
/* Card Styles */
.style-ocean { background: linear-gradient(135deg, #0284c7, #0f172a); }
.style-onyx { background: linear-gradient(135deg, #3f3f46, #18181b); }
.style-ruby { background: linear-gradient(135deg, #9f1239, #4c0519); }
.style-emerald { background: linear-gradient(135deg, #047857, #064e3b); }
.style-gold { background: linear-gradient(135deg, #ca8a04, #713f12); }
.credit-card::after { content: ''; position: absolute; top: -50px; right: -50px; width: 200px; height: 200px; background: rgba(255,255,255,0.05); border-radius: 50%; pointer-events: none;}
.cc-top { display: flex; justify-content: space-between; align-items: flex-start; margin-bottom: 32px; position: relative; z-index: 2;}
.cc-top h4 { font-size: 1.2rem; }
.cc-top p { font-size: 0.8rem; color: rgba(255,255,255,0.7); }
.cc-chip { width: 45px; height: 35px; background: linear-gradient(135deg, #eab308, #ca8a04); border-radius: 6px; }
.cc-number { font-size: 1.6rem; letter-spacing: 3px; margin-bottom: 32px; font-family: monospace; text-shadow: 0 2px 4px rgba(0,0,0,0.3); position: relative; z-index: 2;}
.cc-bottom { display: flex; justify-content: space-between; font-size: 0.8rem; color: rgba(255,255,255,0.7); position: relative; z-index: 2;}
.cc-bottom h5 { font-size: 1rem; color: white; margin-top: 4px; text-transform: uppercase; letter-spacing: 1px;}
.card-status { position: absolute; top: 24px; right: 24px; background: rgba(0,0,0,0.5); padding: 4px 12px; border-radius: 12px; font-size: 0.7rem; font-weight: bold; border: 1px solid rgba(255,255,255,0.2); z-index: 3;}
.card-status.pending { color: var(--accent-yellow); }
.card-status.blocked { color: var(--danger); }
.swipe-indicators { display: flex; justify-content: center; gap: 6px; margin-top: -8px; margin-bottom: 24px;}
.dot { width: 6px; height: 6px; background: rgba(255,255,255,0.2); border-radius: 50%; transition: 0.3s; }
.dot.active { background: var(--text-main); width: 16px; border-radius: 4px; }
/* Controls & Settings */
.card-controls { display: grid; grid-template-columns: repeat(4, 1fr); gap: 12px; margin-bottom: 32px; }
.control-btn { background: var(--surface-dark); border: 1px solid rgba(255,255,255,0.05); padding: 16px 8px; border-radius: 16px; display: flex; flex-direction: column; align-items: center; gap: 8px; color: var(--text-main); font-size: 0.75rem; cursor: pointer; transition: 0.2s;}
.control-btn:active { transform: scale(0.95); background: var(--surface-light); }
.control-btn.disabled { opacity: 0.5; pointer-events: none; }
.section-header { font-size: 1.1rem; font-weight: 600; margin-bottom: 16px; }
.settings-card { background: var(--surface-dark); border-radius: 16px; padding: 0 16px; border: 1px solid rgba(255,255,255,0.05); margin-bottom: 24px; }
.setting-row { display: flex; justify-content: space-between; align-items: center; padding: 16px 0; border-bottom: 1px solid rgba(255,255,255,0.05); }
.setting-row:last-child { border-bottom: none; }
.setting-info h4 { font-size: 0.95rem; margin-bottom: 4px; }
.setting-info p { font-size: 0.75rem; color: var(--text-muted); }
.toggle-switch { position: relative; display: inline-block; width: 44px; height: 24px; }
.toggle-switch input { opacity: 0; width: 0; height: 0; }
.slider { position: absolute; cursor: pointer; top: 0; left: 0; right: 0; bottom: 0; background-color: rgba(255,255,255,0.1); transition: .4s; border-radius: 34px; }
.slider:before { position: absolute; content: ""; height: 18px; width: 18px; left: 3px; bottom: 3px; background-color: white; transition: .4s; border-radius: 50%; }
input:checked + .slider { background-color: var(--accent-blue); }
input:checked + .slider.danger { background-color: var(--danger); }
input:checked + .slider:before { transform: translateX(20px); }
.limit-header { display: flex; justify-content: space-between; font-size: 0.85rem; margin-bottom: 8px; }
.limit-bar-bg { width: 100%; height: 8px; background: rgba(255,255,255,0.1); border-radius: 4px; overflow: hidden; }
.limit-bar-fill { height: 100%; background: var(--accent-blue); border-radius: 4px; transition: width 0.4s ease;}
/* Modals & Forms */
.modal-overlay { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0, 0, 0, 0.6); backdrop-filter: blur(4px); display: flex; align-items: flex-end; justify-content: center; opacity: 0; pointer-events: none; transition: 0.3s ease-in-out; z-index: 1000; }
.modal-overlay.active { opacity: 1; pointer-events: all; }
.modal-container { background: var(--surface-dark); width: 100%; max-width: 500px; border-radius: 24px 24px 0 0; padding: 24px; transform: translateY(100%); transition: 0.3s ease-in-out; border-top: 1px solid rgba(255,255,255,0.05); max-height: 90vh; overflow-y: auto;}
.modal-overlay.active .modal-container { transform: translateY(0); }
.modal-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 4px; }
.modal-header h3 { font-size: 1.2rem; }
.close-btn { background: rgba(255,255,255,0.1); border: none; color: white; width: 30px; height: 30px; border-radius: 50%; font-size: 1.2rem; cursor: pointer; display: flex; align-items: center; justify-content: center; }
.modal-subtitle { color: var(--text-muted); font-size: 0.85rem; margin-bottom: 24px; }
.form-group { margin-bottom: 20px; }
.form-label { display: block; font-size: 0.85rem; color: var(--text-muted); margin-bottom: 8px; }
.form-input { width: 100%; background: var(--bg-dark); border: 1px solid rgba(255,255,255,0.1); color: var(--text-main); padding: 16px; border-radius: 12px; font-size: 1rem; outline: none; transition: 0.2s;}
select.form-input { appearance: none; cursor: pointer;}
.style-selector { display: flex; gap: 12px; margin-bottom: 24px; overflow-x: auto; padding-bottom: 8px;}
.style-option { width: 60px; height: 40px; border-radius: 8px; cursor: pointer; border: 2px solid transparent; flex-shrink: 0; transition: 0.2s;}
.style-option.selected { border-color: white; transform: scale(1.1); box-shadow: 0 4px 10px rgba(0,0,0,0.5);}
.primary-btn { width: 100%; background: var(--accent-blue); color: white; border: none; padding: 16px; border-radius: 12px; font-size: 1rem; font-weight: 600; cursor: pointer; transition: 0.2s; margin-top: 8px;}
.primary-btn:active { transform: scale(0.98); }
.toast { position: fixed; top: -100px; left: 50%; transform: translateX(-50%); background: var(--success); color: white; padding: 12px 24px; border-radius: 30px; font-size: 0.9rem; font-weight: 600; box-shadow: 0 4px 12px rgba(0,0,0,0.3); transition: 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275); z-index: 2000; text-align: center;}
.toast.show { top: 40px; }
.toast.error { background: var(--danger); }
.bottom-nav { position: fixed; bottom: 0; width: 100%; background: rgba(19, 26, 42, 0.95); backdrop-filter: blur(10px); display: flex; justify-content: space-around; padding: 12px 0 24px 0; border-top: 1px solid rgba(255,255,255,0.05); z-index: 100; }
.nav-item { display: flex; flex-direction: column; align-items: center; color: var(--text-muted); text-decoration: none; font-size: 0.7rem; gap: 4px; }
.nav-item.active { color: var(--accent-blue); }
.no-cards-msg { text-align: center; padding: 40px 20px; color: var(--text-muted); background: var(--surface-dark); border-radius: 16px; border: 1px dashed rgba(255,255,255,0.1); margin-bottom: 24px;}
@media (min-width: 1024px) {
.app-container { flex-direction: row; }
.bottom-nav { display: none; }
.sidebar { display: flex; flex-direction: column; width: 260px; background: var(--surface-dark); border-right: 1px solid rgba(255,255,255,0.05); padding: 32px 24px; height: 100vh; position: sticky; top: 0; }
.sidebar .logo { font-size: 1.5rem; font-weight: bold; margin-bottom: 48px; color: var(--accent-blue); }
.side-nav { display: flex; flex-direction: column; gap: 12px; }
.side-nav a { color: var(--text-muted); text-decoration: none; padding: 12px 16px; border-radius: 12px; transition: 0.2s; display: flex; align-items: center; gap: 12px; }
.side-nav a:hover, .side-nav a.active { background: rgba(14, 165, 233, 0.1); color: var(--accent-blue); }
.main-content { padding: 40px 60px; max-width: 800px; margin: 0 auto; }
.card-carousel { overflow-x: visible; gap: 32px; }
.credit-card { min-width: calc(50% - 16px); }
.swipe-indicators { display: none; }
}
</style>
</head>
<body>
<?php if ($kyc_status === 'pending' || $kyc_status === 'rejected'): ?>
<style> body { overflow: hidden !important; } .restriction-overlay { position: fixed; top: 0; left: 0; width: 100vw; height: 100vh; background: rgba(10, 14, 23, 0.85); backdrop-filter: blur(12px); z-index: 9999; display: flex; align-items: center; justify-content: center; padding: 20px; } .restriction-box { background: var(--surface-dark); border: 1px solid rgba(255,255,255,0.1); padding: 40px; border-radius: 24px; text-align: center; max-width: 450px; box-shadow: 0 20px 50px rgba(0,0,0,0.8); } .logout-btn-large { display: inline-block; width: 100%; background: rgba(239, 68, 68, 0.1); color: var(--danger); border: 1px solid rgba(239, 68, 68, 0.3); padding: 16px; border-radius: 12px; text-decoration: none; font-weight: 600; margin-top: 20px;} </style>
<div class="restriction-overlay">
<div class="restriction-box">
<div style="font-size: 3rem; margin-bottom: 16px;">⏳</div>
<h2 style="margin-bottom: 12px;">Security Check</h2>
<p style="color: var(--text-muted); font-size: 0.95rem;">Hello, <?php echo htmlspecialchars($_SESSION['first_name']); ?>. Card management is locked until your identity verification is complete.</p>
<a href="logout.php" class="logout-btn-large">Sign Out</a>
</div>
</div>
<?php endif; ?>
<div id="toast" class="toast"></div>
<div class="app-container">
<aside class="sidebar">
<div class="logo">City Prime</div>
<nav class="side-nav">
<a href="index.php"><span>🏠</span> Home</a>
<a href="activity.php"><span>📊</span> Activity</a>
<a href="transfer.php"><span>💸</span> Transfer</a>
<a href="cards.php" class="active"><span>💳</span> Cards</a>
<a href="profile.php"><span>👤</span> Profile</a>
</nav>
</aside>
<main class="main-content">
<header class="top-header">
<h1 class="page-title">Card Center</h1>
<div class="add-card-btn" id="openCreateCardBtn">+</div>
</header>
<?php if (empty($userCards)): ?>
<div class="no-cards-msg">
<div style="font-size: 2rem; margin-bottom: 12px;">💳</div>
<h3>No Cards Found</h3>
<p style="margin-top: 8px; font-size: 0.85rem;">Click the + button above to issue your first City Prime card.</p>
</div>
<?php else: ?>
<div class="card-carousel" id="cardSlider">
<?php foreach ($userCards as $index => $card): ?>
<div class="credit-card style-<?php echo htmlspecialchars($card['card_style']); ?>" id="card-ui-<?php echo $card['id']; ?>">
<?php if($card['status'] === 'pending'): ?>
<div class="card-status pending">PENDING</div>
<?php elseif($card['status'] === 'blocked'): ?>
<div class="card-status blocked">BLOCKED</div>
<?php endif; ?>
<div class="cc-top">
<div>
<h4><?php echo htmlspecialchars($card['card_type']); ?></h4>
<p id="frozen-text-<?php echo $card['id']; ?>">
<?php echo $card['is_frozen'] ? '<span style="color:var(--danger); font-weight:bold;">FROZEN</span>' : 'Active Card'; ?>
</p>
</div>
<div class="cc-chip"></div>
</div>
<div class="cc-number" id="cc-num-<?php echo $card['id']; ?>">**** **** **** <?php echo substr($card['card_number'], -4); ?></div>
<div class="cc-bottom">
<div><p>Card Holder</p><h5><?php echo htmlspecialchars($fullName); ?></h5></div>
<div><p>Valid</p><h5><?php echo htmlspecialchars($card['expiry_date']); ?></h5></div>
</div>
</div>
<?php endforeach; ?>
</div>
<div class="swipe-indicators" id="swipeIndicators">
<?php foreach ($userCards as $index => $card): ?>
<span class="dot <?php echo $index === 0 ? 'active' : ''; ?>"></span>
<?php endforeach; ?>
</div>
<div id="cardSettingsBlock">
<div class="card-controls">
<button class="control-btn" id="btn-details" onclick="toggleDetails()">
<span class="control-icon">👁️</span><span id="detailsText">Details</span>
</button>
<button class="control-btn" id="btn-limit" onclick="promptNewLimit()">
<span class="control-icon">📊</span><span>Set Limit</span>
</button>
<button class="control-btn" id="btn-cvv" onclick="showCVV()">
<span class="control-icon">🔑</span><span>Show CVV</span>
</button>
<button class="control-btn" id="btn-design" onclick="openDesignModal()">
<span class="control-icon">🎨</span><span>Design</span>
</button>
</div>
<h3 class="section-header">Monthly Spending Limit</h3>
<div class="settings-card" style="padding: 20px 16px;">
<div class="limit-header">
<span style="color: var(--text-muted);" id="ui-spent">Spent $0</span>
<strong id="ui-limit">$0 Max</strong>
</div>
<div class="limit-bar-bg">
<div class="limit-bar-fill" id="ui-bar" style="width: 0%;"></div>
</div>
</div>
<h3 class="section-header">Security Settings</h3>
<div class="settings-card">
<div class="setting-row">
<div class="setting-info">
<h4 style="color: var(--danger);">Freeze Card</h4>
<p>Temporarily disable all transactions</p>
</div>
<label class="toggle-switch">
<input type="checkbox" id="toggle-is_frozen" onchange="updateSetting('is_frozen', this.checked)">
<span class="slider danger"></span>
</label>
</div>
<div class="setting-row">
<div class="setting-info">
<h4>Online Payments</h4>
<p>Allow internet purchases</p>
</div>
<label class="toggle-switch">
<input type="checkbox" id="toggle-allow_online" onchange="updateSetting('allow_online', this.checked)">
<span class="slider"></span>
</label>
</div>
<div class="setting-row">
<div class="setting-info">
<h4>ATM Withdrawals</h4>
<p>Allow cash withdrawals</p>
</div>
<label class="toggle-switch">
<input type="checkbox" id="toggle-allow_atm" onchange="updateSetting('allow_atm', this.checked)">
<span class="slider"></span>
</label>
</div>
</div>
</div>
<?php endif; ?>
</main>
<nav class="bottom-nav">
<a href="activity.php" class="nav-item"><span class="nav-icon">📊</span>Activity</a>
<a href="transfer.php" class="nav-item"><span class="nav-icon">💸</span>Transfer</a>
<a href="index.php" class="nav-item"><span class="nav-icon">🏠</span>Home</a>
<a href="cards.php" class="nav-item active"><span class="nav-icon">💳</span>Cards</a>
<a href="profile.php" class="nav-item"><span class="nav-icon">👤</span>Profile</a>
</nav>
</div>
<div class="modal-overlay" id="createCardModal">
<div class="modal-container">
<div class="modal-header">
<h3>Request New Card</h3>
<button class="close-btn" onclick="document.getElementById('createCardModal').classList.remove('active')">×</button>
</div>
<p class="modal-subtitle">Select your preferred card type and design.</p>
<form action="cards.php" method="POST">
<input type="hidden" name="create_card" value="true">
<input type="hidden" name="card_style" id="selectedStyleInput" value="ocean">
<div class="form-group">
<label class="form-label">Select Card Type</label>
<select name="card_type" class="form-input" id="cardTypeSelect" required onchange="updateCardFee()">
<option value="" disabled selected>Choose Network...</option>
<option value="mastercard" data-fee="15.00" data-yearly="10.00" data-min="50.00">Mastercard Standard</option>
<option value="visa" data-fee="12.00" data-yearly="8.00" data-min="30.00">Visa Classic</option>
<option value="amex" data-fee="50.00" data-yearly="150.00" data-min="500.00">American Express Gold</option>
<option value="verve" data-fee="5.00" data-yearly="2.00" data-min="10.00">Verve Local</option>
</select>
</div>
<div class="form-group">
<label class="form-label">Select Card Wallpaper</label>
<div class="style-selector">
<div class="style-option style-ocean selected" data-style="ocean" onclick="selectStyle(this, 'selectedStyleInput')"></div>
<div class="style-option style-onyx" data-style="onyx" onclick="selectStyle(this, 'selectedStyleInput')"></div>
<div class="style-option style-ruby" data-style="ruby" onclick="selectStyle(this, 'selectedStyleInput')"></div>
<div class="style-option style-emerald" data-style="emerald" onclick="selectStyle(this, 'selectedStyleInput')"></div>
<div class="style-option style-gold" data-style="gold" onclick="selectStyle(this, 'selectedStyleInput')"></div>
</div>
</div>
<div style="background: rgba(255,255,255,0.03); border: 1px solid rgba(255,255,255,0.05); padding: 16px; border-radius: 12px; margin-bottom: 24px;">
<div style="display: flex; justify-content: space-between; font-size: 0.85rem; margin-bottom: 8px;">
<span style="color: var(--text-muted);">Issuance Fee (Deducted Now):</span>
<strong id="displayFee">$0.00</strong>
</div>
<div style="display: flex; justify-content: space-between; font-size: 0.85rem; margin-bottom: 8px;">
<span style="color: var(--text-muted);">Yearly Maintenance Fee:</span>
<strong id="displayYearly">$0.00</strong>
</div>
<div style="display: flex; justify-content: space-between; font-size: 0.85rem;">
<span style="color: var(--text-muted);">Required Min Balance:</span>
<strong id="displayMin">$0.00</strong>
</div>
</div>
<button type="submit" class="primary-btn">Issue Card</button>
</form>
</div>
</div>
<div class="modal-overlay" id="editDesignModal">
<div class="modal-container">
<div class="modal-header">
<h3>Edit Card Design</h3>
<button class="close-btn" onclick="document.getElementById('editDesignModal').classList.remove('active')">×</button>
</div>
<p class="modal-subtitle">Update the wallpaper for your active card.</p>
<div class="form-group">
<div class="style-selector" id="editStyleContainer">
<div class="style-option style-ocean" data-style="ocean" onclick="selectStyle(this, 'editStyleInput')"></div>
<div class="style-option style-onyx" data-style="onyx" onclick="selectStyle(this, 'editStyleInput')"></div>
<div class="style-option style-ruby" data-style="ruby" onclick="selectStyle(this, 'editStyleInput')"></div>
<div class="style-option style-emerald" data-style="emerald" onclick="selectStyle(this, 'editStyleInput')"></div>
<div class="style-option style-gold" data-style="gold" onclick="selectStyle(this, 'editStyleInput')"></div>
</div>
<input type="hidden" id="editStyleInput" value="">
</div>
<button class="primary-btn" onclick="saveNewDesign()">Save Design</button>
</div>
</div>
<script>
// --- Pass PHP Data to Javascript securely ---
const userCards = <?php echo $cardsJson; ?>;
let activeCardIndex = 0;
let activeCard = userCards.length > 0 ? userCards[0] : null;
let showingDetails = false;
function showToast(msg, isError = false) {
const toast = document.getElementById('toast');
toast.textContent = msg;
toast.className = 'toast show ' + (isError ? 'error' : '');
setTimeout(() => { toast.classList.remove('show'); }, 3000);
}
// --- Dynamic UI Updater based on Selected Card ---
function updateUIForActiveCard() {
if (!activeCard) return;
// Reset details view
showingDetails = false;
document.getElementById('detailsText').innerText = "Details";
document.getElementById('cc-num-' + activeCard.id).innerText = "**** **** **** " + activeCard.card_number.slice(-4);
// Update Limit Progress Bar
const limit = parseFloat(activeCard.spending_limit);
const spent = parseFloat(activeCard.amount_spent);
const percent = limit > 0 ? (spent / limit) * 100 : 0;
document.getElementById('ui-spent').innerText = "Spent $" + spent.toFixed(2);
document.getElementById('ui-limit').innerText = "$" + limit.toLocaleString() + " Max";
document.getElementById('ui-bar').style.width = Math.min(percent, 100) + "%";
// Update Toggles to match database
document.getElementById('toggle-is_frozen').checked = (activeCard.is_frozen == 1);
document.getElementById('toggle-allow_online').checked = (activeCard.allow_online == 1);
document.getElementById('toggle-allow_atm').checked = (activeCard.allow_atm == 1);
// Update text if frozen
const freezeText = document.getElementById('frozen-text-' + activeCard.id);
if (freezeText) {
freezeText.innerHTML = activeCard.is_frozen == 1 ? '<span style="color:var(--danger); font-weight:bold;">FROZEN</span>' : 'Active Card';
}
// Disable controls if pending
const isPending = (activeCard.status === 'pending');
document.getElementById('btn-details').classList.toggle('disabled', isPending);
document.getElementById('btn-cvv').classList.toggle('disabled', isPending);
}
// --- Handle Carousel Swiping ---
const slider = document.getElementById('cardSlider');
const dots = document.querySelectorAll('.swipe-indicators .dot');
if (slider && userCards.length > 0) {
updateUIForActiveCard(); // Initialize first card
slider.addEventListener('scroll', () => {
const scrollPosition = slider.scrollLeft;
const slideWidth = slider.clientWidth;
const newIndex = Math.round(scrollPosition / slideWidth);
if (newIndex !== activeCardIndex && userCards[newIndex]) {
activeCardIndex = newIndex;
activeCard = userCards[activeCardIndex];
dots.forEach((dot, index) => { dot.classList.toggle('active', index === activeIndex); });
updateUIForActiveCard();
}
});
}
// --- AJAX: Update Database when Toggles are clicked ---
function updateSetting(settingName, isChecked) {
if (!activeCard) return;
if (settingName === 'is_frozen' && isChecked) {
if(!confirm("Freeze this card? All future transactions will be declined.")) {
document.getElementById('toggle-is_frozen').checked = false;
return;
}
}
const formData = new FormData();
formData.append('ajax_action', 'toggle_setting');
formData.append('card_id', activeCard.id);
formData.append('setting', settingName);
formData.append('value', isChecked ? '1' : '0');
fetch('cards.php', { method: 'POST', body: formData })
.then(res => res.json())
.then(data => {
if(data.success) {
showToast(data.message);
activeCard[settingName] = isChecked ? 1 : 0;
updateUIForActiveCard();
} else {
showToast(data.message, true);
document.getElementById('toggle-' + settingName).checked = !isChecked;
}
});
}
// --- AJAX: Update Spending Limit ---
function promptNewLimit() {
if (!activeCard) return;
const currentLimit = activeCard.spending_limit;
const newLimit = prompt(`Enter new monthly limit for this card (Current: $${currentLimit}):`, currentLimit);
if (newLimit !== null && !isNaN(newLimit) && newLimit > 0) {
const formData = new FormData();
formData.append('ajax_action', 'update_limit');
formData.append('card_id', activeCard.id);
formData.append('new_limit', newLimit);
fetch('cards.php', { method: 'POST', body: formData })
.then(res => res.json())
.then(data => {
if(data.success) {
showToast(data.message);
activeCard.spending_limit = newLimit;
updateUIForActiveCard();
} else {
showToast(data.message, true);
}
});
}
}
// --- Reveal Card Details visually ---
function toggleDetails() {
if (!activeCard || activeCard.status === 'pending') return;
const numberEl = document.getElementById('cc-num-' + activeCard.id);
const textEl = document.getElementById('detailsText');
showingDetails = !showingDetails;
if(showingDetails) {
numberEl.innerText = activeCard.card_number;
textEl.innerText = "Hide";
} else {
numberEl.innerText = "**** **** **** " + activeCard.card_number.slice(-4);
textEl.innerText = "Details";
}
}
function showCVV() {
if (!activeCard || activeCard.status === 'pending') return;
alert("Your CVV is: " + activeCard.cvv + "\n\nPlease keep this secure.");
}
// --- Create Card UI Logic ---
document.getElementById('openCreateCardBtn').addEventListener('click', () => {
document.getElementById('createCardModal').classList.add('active');
});
function updateCardFee() {
const select = document.getElementById('cardTypeSelect');
const opt = select.options[select.selectedIndex];
if (opt.value) {
document.getElementById('displayFee').innerText = "$" + opt.getAttribute('data-fee');
document.getElementById('displayYearly').innerText = "$" + opt.getAttribute('data-yearly') + " / yr";
document.getElementById('displayMin').innerText = "$" + opt.getAttribute('data-min');
}
}
function selectStyle(element, inputId) {
element.parentElement.querySelectorAll('.style-option').forEach(el => el.classList.remove('selected'));
element.classList.add('selected');
document.getElementById(inputId).value = element.getAttribute('data-style');
}
// --- Edit Design Logic ---
function openDesignModal() {
if (!activeCard) return;
document.getElementById('editDesignModal').classList.add('active');
// Pre-select current style
document.querySelectorAll('#editStyleContainer .style-option').forEach(el => {
el.classList.remove('selected');
if(el.getAttribute('data-style') === activeCard.card_style) {
el.classList.add('selected');
}
});
document.getElementById('editStyleInput').value = activeCard.card_style;
}
function saveNewDesign() {
const newStyle = document.getElementById('editStyleInput').value;
if (!activeCard || activeCard.card_style === newStyle) {
document.getElementById('editDesignModal').classList.remove('active');
return;
}
const formData = new FormData();
formData.append('ajax_action', 'update_style');
formData.append('card_id', activeCard.id);
formData.append('new_style', newStyle);
fetch('cards.php', { method: 'POST', body: formData })
.then(res => res.json())
.then(data => {
if(data.success) {
showToast(data.message);
// Update UI Class
const cardEl = document.getElementById('card-ui-' + activeCard.id);
cardEl.className = 'credit-card style-' + newStyle;
// Update JS state
activeCard.card_style = newStyle;
document.getElementById('editDesignModal').classList.remove('active');
} else {
showToast(data.message, true);
}
});
}
// PHP Output Toasts
<?php if ($actionSuccess): ?> showToast("<?php echo $actionMessage; ?>"); <?php endif; ?>
<?php if (!empty($actionError)): ?> showToast("<?php echo $actionError; ?>", true); <?php endif; ?>
</script>
</body>
</html>
b IDATxytVսϓ22 A@IR:hCiZ[v*E:WũZA ^dQeQ @ !jZ'>gsV仿$|?g)&x-E