Autosave: 20260213-065133

This commit is contained in:
Flatlogic Bot 2026-02-13 06:51:33 +00:00
parent 11b008ae62
commit 8171e13d85
12 changed files with 1374 additions and 141 deletions

30
admin/auth.php Normal file
View File

@ -0,0 +1,30 @@
<?php
// Secure session configuration
ini_set('session.cookie_httponly', 1);
ini_set('session.use_only_cookies', 1);
ini_set('session.cookie_path', '/');
if (session_status() === PHP_SESSION_NONE) {
session_start();
}
function is_logged_in() {
return isset($_SESSION['user_id']);
}
function require_login() {
if (!is_logged_in()) {
header('Location: login.php?auth_error=1');
exit;
}
}
function get_user() {
if (!is_logged_in()) return null;
return $_SESSION['user'];
}
function is_super_admin() {
$user = get_user();
return $user && $user['role'] === 'super_admin';
}

245
admin/cases.php Normal file
View File

@ -0,0 +1,245 @@
<?php
require_once 'auth.php';
require_once '../db/config.php';
require_login();
$user = get_user();
$pdo = db();
$msg = '';
// Handle Delete
if (isset($_GET['delete'])) {
$id = (int)$_GET['delete'];
if (is_super_admin()) {
$pdo->prepare("DELETE FROM cases WHERE id = ?")->execute([$id]);
} else {
$pdo->prepare("DELETE FROM cases WHERE id = ? AND org_id = ?")->execute([$id, $user['org_id']]);
}
header('Location: cases.php?success=deleted');
exit;
}
// Fetch cases
if (is_super_admin()) {
$cases = $pdo->query("SELECT c.*, o.name_en as org_name FROM cases c JOIN organizations o ON c.org_id = o.id")->fetchAll();
$orgs = $pdo->query("SELECT * FROM organizations")->fetchAll();
} else {
$cases = $pdo->prepare("SELECT c.*, o.name_en as org_name FROM cases c JOIN organizations o ON c.org_id = o.id WHERE c.org_id = ?");
$cases->execute([$user['org_id']]);
$cases = $cases->fetchAll();
$orgs = []; // Org admin doesn't need to select org
}
// Handle Add/Edit
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$id = $_POST['id'] ?? null;
$org_id = is_super_admin() ? $_POST['org_id'] : $user['org_id'];
$title_en = $_POST['title_en'];
$title_ar = $_POST['title_ar'];
$desc_en = $_POST['desc_en'];
$desc_ar = $_POST['desc_ar'];
$goal = $_POST['goal'];
$image_url = $_POST['image_url'];
if ($id) {
$stmt = $pdo->prepare("UPDATE cases SET org_id=?, title_en=?, title_ar=?, desc_en=?, desc_ar=?, goal=?, image_url=? WHERE id=?");
$stmt->execute([$org_id, $title_en, $title_ar, $desc_en, $desc_ar, $goal, $image_url, $id]);
} else {
$stmt = $pdo->prepare("INSERT INTO cases (org_id, title_en, title_ar, desc_en, desc_ar, goal, raised, image_url) VALUES (?, ?, ?, ?, ?, ?, 0, ?)");
$stmt->execute([$org_id, $title_en, $title_ar, $desc_en, $desc_ar, $goal, $image_url]);
}
header('Location: cases.php?success=saved');
exit;
}
$edit_case = null;
if (isset($_GET['edit'])) {
$id = (int)$_GET['edit'];
if (is_super_admin()) {
$stmt = $pdo->prepare("SELECT * FROM cases WHERE id = ?");
$stmt->execute([$id]);
} else {
$stmt = $pdo->prepare("SELECT * FROM cases WHERE id = ? AND org_id = ?");
$stmt->execute([$id, $user['org_id']]);
}
$edit_case = $stmt->fetch();
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Manage Cases - CharityHub Admin</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.1/font/bootstrap-icons.css">
<style>
:root { --sidebar-width: 260px; --primary-color: #059669; }
body { background-color: #f3f4f6; }
.sidebar { width: var(--sidebar-width); height: 100vh; position: fixed; left: 0; top: 0; background: #111827; color: #fff; padding: 1.5rem; }
.main-content { margin-left: var(--sidebar-width); padding: 2rem; }
.nav-link { color: #9ca3af; margin-bottom: 0.5rem; border-radius: 8px; }
.nav-link:hover, .nav-link.active { color: #fff; background: #1f2937; }
.nav-link.active { background: var(--primary-color); }
.card { border: none; border-radius: 12px; box-shadow: 0 1px 3px rgba(0,0,0,0.1); }
</style>
</head>
<body>
<div class="sidebar">
<h4 class="mb-4">CharityHub</h4>
<nav class="nav flex-column">
<a href="index.php" class="nav-link"><i class="bi bi-speedometer2 me-2"></i> Dashboard</a>
<?php if (is_super_admin()): ?>
<a href="organizations.php" class="nav-link"><i class="bi bi-building me-2"></i> Organizations</a>
<?php endif; ?>
<a href="cases.php" class="nav-link active"><i class="bi bi-grid me-2"></i> Cases</a>
<a href="donations.php" class="nav-link"><i class="bi bi-cash-stack me-2"></i> Donations</a>
<hr>
<a href="logout.php" class="nav-link"><i class="bi bi-box-arrow-right me-2"></i> Logout</a>
</nav>
</div>
<div class="main-content">
<div class="d-flex justify-content-between align-items-center mb-4">
<h2>Manage Donation Cases</h2>
<button class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#caseModal" onclick="clearForm()">
<i class="bi bi-plus-lg me-1"></i> New Case
</button>
</div>
<?php if (isset($_GET['success'])): ?>
<div class="alert alert-success alert-dismissible fade show">
Operation successful!
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
</div>
<?php endif; ?>
<div class="card p-0">
<div class="table-responsive">
<table class="table table-hover mb-0">
<thead class="table-light">
<tr>
<th class="ps-4">Title (EN/AR)</th>
<?php if (is_super_admin()): ?><th>Organization</th><?php endif; ?>
<th>Goal</th>
<th>Raised</th>
<th class="text-end pe-4">Actions</th>
</tr>
</thead>
<tbody>
<?php foreach ($cases as $case): ?>
<tr>
<td class="ps-4">
<div><strong><?= htmlspecialchars($case['title_en']) ?></strong></div>
<small class="text-muted"><?= htmlspecialchars($case['title_ar']) ?></small>
</td>
<?php if (is_super_admin()): ?><td><?= htmlspecialchars($case['org_name']) ?></td><?php endif; ?>
<td>$<?= number_format($case['goal']) ?></td>
<td>$<?= number_format($case['raised']) ?></td>
<td class="text-end pe-4">
<a href="?edit=<?= $case['id'] ?>" class="btn btn-sm btn-outline-primary me-1"><i class="bi bi-pencil"></i></a>
<a href="?delete=<?= $case['id'] ?>" class="btn btn-sm btn-outline-danger" onclick="return confirm('Are you sure?')"><i class="bi bi-trash"></i></a>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
</div>
</div>
<!-- Case Modal -->
<div class="modal fade" id="caseModal" tabindex="-1">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<form method="POST">
<div class="modal-header">
<h5 class="modal-title" id="modalTitle">New Case</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<input type="hidden" name="id" id="caseId">
<?php if (is_super_admin()): ?>
<div class="mb-3">
<label class="form-label">Organization</label>
<select name="org_id" id="caseOrg" class="form-select" required>
<?php foreach ($orgs as $org): ?>
<option value="<?= $org['id'] ?>"><?= htmlspecialchars($org['name_en']) ?></option>
<?php endforeach; ?>
</select>
</div>
<?php endif; ?>
<div class="row">
<div class="col-md-6 mb-3">
<label class="form-label">Title (English)</label>
<input type="text" name="title_en" id="caseTitleEn" class="form-control" required>
</div>
<div class="col-md-6 mb-3">
<label class="form-label">Title (Arabic)</label>
<input type="text" name="title_ar" id="caseTitleAr" class="form-control" dir="rtl" required>
</div>
</div>
<div class="row">
<div class="col-md-6 mb-3">
<label class="form-label">Description (English)</label>
<textarea name="desc_en" id="caseDescEn" class="form-control" rows="3"></textarea>
</div>
<div class="col-md-6 mb-3">
<label class="form-label">Description (Arabic)</label>
<textarea name="desc_ar" id="caseDescAr" class="form-control" dir="rtl" rows="3"></textarea>
</div>
</div>
<div class="row">
<div class="col-md-6 mb-3">
<label class="form-label">Goal Amount ($)</label>
<input type="number" name="goal" id="caseGoal" class="form-control" step="0.01" required>
</div>
<div class="col-md-6 mb-3">
<label class="form-label">Image URL</label>
<input type="url" name="image_url" id="caseImageUrl" class="form-control">
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
<button type="submit" class="btn btn-primary">Save Case</button>
</div>
</form>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
<script>
const caseModal = new bootstrap.Modal(document.getElementById('caseModal'));
function clearForm() {
document.getElementById('caseId').value = '';
document.getElementById('caseTitleEn').value = '';
document.getElementById('caseTitleAr').value = '';
document.getElementById('caseDescEn').value = '';
document.getElementById('caseDescAr').value = '';
document.getElementById('caseGoal').value = '';
document.getElementById('caseImageUrl').value = '';
document.getElementById('modalTitle').innerText = 'New Case';
}
<?php if ($edit_case): ?>
window.addEventListener('load', () => {
document.getElementById('caseId').value = '<?= $edit_case['id'] ?>';
document.getElementById('caseTitleEn').value = '<?= addslashes($edit_case['title_en']) ?>';
document.getElementById('caseTitleAr').value = '<?= addslashes($edit_case['title_ar']) ?>';
document.getElementById('caseDescEn').value = '<?= addslashes($edit_case['desc_en']) ?>';
document.getElementById('caseDescAr').value = '<?= addslashes($edit_case['desc_ar']) ?>';
document.getElementById('caseGoal').value = '<?= $edit_case['goal'] ?>';
document.getElementById('caseImageUrl').value = '<?= $edit_case['image_url'] ?>';
<?php if (is_super_admin()): ?>
document.getElementById('caseOrg').value = '<?= $edit_case['org_id'] ?>';
<?php endif; ?>
document.getElementById('modalTitle').innerText = 'Edit Case';
caseModal.show();
});
<?php endif; ?>
</script>
</body>
</html>

111
admin/donations.php Normal file
View File

@ -0,0 +1,111 @@
<?php
require_once 'auth.php';
require_once '../db/config.php';
require_login();
$user = get_user();
$pdo = db();
// Fetch donations
if (is_super_admin()) {
$donations = $pdo->query("SELECT d.*, c.title_en as case_title, o.name_en as org_name
FROM donations d
JOIN cases c ON d.case_id = c.id
JOIN organizations o ON c.org_id = o.id
ORDER BY d.created_at DESC")->fetchAll();
} else {
$donations = $pdo->prepare("SELECT d.*, c.title_en as case_title, o.name_en as org_name
FROM donations d
JOIN cases c ON d.case_id = c.id
JOIN organizations o ON c.org_id = o.id
WHERE c.org_id = ?
ORDER BY d.created_at DESC");
$donations->execute([$user['org_id']]);
$donations = $donations->fetchAll();
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Donations - CharityHub Admin</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.1/font/bootstrap-icons.css">
<style>
:root { --sidebar-width: 260px; --primary-color: #059669; }
body { background-color: #f3f4f6; }
.sidebar { width: var(--sidebar-width); height: 100vh; position: fixed; left: 0; top: 0; background: #111827; color: #fff; padding: 1.5rem; }
.main-content { margin-left: var(--sidebar-width); padding: 2rem; }
.nav-link { color: #9ca3af; margin-bottom: 0.5rem; border-radius: 8px; }
.nav-link:hover, .nav-link.active { color: #fff; background: #1f2937; }
.nav-link.active { background: var(--primary-color); }
.card { border: none; border-radius: 12px; box-shadow: 0 1px 3px rgba(0,0,0,0.1); }
.badge-completed { background-color: #d1fae5; color: #065f46; }
.badge-pending { background-color: #fef3c7; color: #92400e; }
.badge-failed { background-color: #fee2e2; color: #991b1b; }
</style>
</head>
<body>
<div class="sidebar">
<h4 class="mb-4">CharityHub</h4>
<nav class="nav flex-column">
<a href="index.php" class="nav-link"><i class="bi bi-speedometer2 me-2"></i> Dashboard</a>
<?php if (is_super_admin()): ?>
<a href="organizations.php" class="nav-link"><i class="bi bi-building me-2"></i> Organizations</a>
<?php endif; ?>
<a href="cases.php" class="nav-link"><i class="bi bi-grid me-2"></i> Cases</a>
<a href="donations.php" class="nav-link active"><i class="bi bi-cash-stack me-2"></i> Donations</a>
<hr>
<a href="logout.php" class="nav-link"><i class="bi bi-box-arrow-right me-2"></i> Logout</a>
</nav>
</div>
<div class="main-content">
<div class="d-flex justify-content-between align-items-center mb-4">
<h2>Donations History</h2>
</div>
<div class="card p-0">
<div class="table-responsive">
<table class="table table-hover mb-0">
<thead class="table-light">
<tr>
<th class="ps-4">Donor</th>
<th>Case</th>
<?php if (is_super_admin()): ?><th>Organization</th><?php endif; ?>
<th>Amount</th>
<th>Status</th>
<th>Date</th>
</tr>
</thead>
<tbody>
<?php foreach ($donations as $don): ?>
<tr>
<td class="ps-4">
<div><strong><?= htmlspecialchars($don['donor_name'] ?: 'Anonymous') ?></strong></div>
<small class="text-muted"><?= htmlspecialchars($don['donor_email']) ?></small>
</td>
<td><?= htmlspecialchars($don['case_title']) ?></td>
<?php if (is_super_admin()): ?><td><?= htmlspecialchars($don['org_name']) ?></td><?php endif; ?>
<td>$<?= number_format($don['amount'], 2) ?></td>
<td>
<span class="badge badge-<?= $don['status'] ?>">
<?= ucfirst($don['status']) ?>
</span>
</td>
<td><?= date('Y-m-d H:i', strtotime($don['created_at'])) ?></td>
</tr>
<?php endforeach; ?>
<?php if (empty($donations)): ?>
<tr>
<td colspan="<?= is_super_admin() ? 6 : 5 ?>" class="text-center py-4 text-muted">No donations found.</td>
</tr>
<?php endif; ?>
</tbody>
</table>
</div>
</div>
</div>
</body>
</html>

107
admin/index.php Normal file
View File

@ -0,0 +1,107 @@
<?php
require_once 'auth.php';
require_once '../db/config.php';
require_login();
$user = get_user();
$pdo = db();
// Fetch stats
if (is_super_admin()) {
$total_orgs = $pdo->query("SELECT COUNT(*) FROM organizations")->fetchColumn();
$total_cases = $pdo->query("SELECT COUNT(*) FROM cases")->fetchColumn();
$total_donations = $pdo->query("SELECT SUM(amount) FROM donations WHERE status = 'completed'")->fetchColumn() ?: 0;
} else {
$org_id = $user['org_id'];
$total_cases = $pdo->query("SELECT COUNT(*) FROM cases WHERE org_id = $org_id")->fetchColumn();
$total_donations = $pdo->query("SELECT SUM(d.amount) FROM donations d JOIN cases c ON d.case_id = c.id WHERE c.org_id = $org_id AND d.status = 'completed'")->fetchColumn() ?: 0;
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Dashboard - CharityHub Admin</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.1/font/bootstrap-icons.css">
<style>
:root { --sidebar-width: 260px; --primary-color: #059669; }
body { background-color: #f3f4f6; }
.sidebar { width: var(--sidebar-width); height: 100vh; position: fixed; left: 0; top: 0; background: #111827; color: #fff; padding: 1.5rem; }
.main-content { margin-left: var(--sidebar-width); padding: 2rem; }
.nav-link { color: #9ca3af; margin-bottom: 0.5rem; border-radius: 8px; }
.nav-link:hover, .nav-link.active { color: #fff; background: #1f2937; }
.nav-link.active { background: var(--primary-color); }
.card { border: none; border-radius: 12px; box-shadow: 0 1px 3px rgba(0,0,0,0.1); }
.stat-card { padding: 1.5rem; }
.stat-icon { font-size: 2rem; color: var(--primary-color); }
</style>
</head>
<body>
<div class="sidebar">
<h4 class="mb-4">CharityHub</h4>
<nav class="nav flex-column">
<a href="index.php" class="nav-link active"><i class="bi bi-speedometer2 me-2"></i> Dashboard</a>
<?php if (is_super_admin()): ?>
<a href="organizations.php" class="nav-link"><i class="bi bi-building me-2"></i> Organizations</a>
<?php endif; ?>
<a href="cases.php" class="nav-link"><i class="bi bi-grid me-2"></i> Cases</a>
<a href="donations.php" class="nav-link"><i class="bi bi-cash-stack me-2"></i> Donations</a>
<hr>
<a href="logout.php" class="nav-link"><i class="bi bi-box-arrow-right me-2"></i> Logout</a>
</nav>
</div>
<div class="main-content">
<div class="d-flex justify-content-between align-items-center mb-4">
<h2>Welcome, <?= is_super_admin() ? 'Super Admin' : 'Org Admin' ?></h2>
<div class="text-muted"><?= date('l, F j, Y') ?></div>
</div>
<div class="row g-4 mb-4">
<?php if (is_super_admin()): ?>
<div class="col-md-4">
<div class="card stat-card">
<div class="d-flex justify-content-between align-items-center">
<div>
<div class="text-muted small">Organizations</div>
<div class="h3 mb-0"><?= $total_orgs ?></div>
</div>
<i class="bi bi-building stat-icon"></i>
</div>
</div>
</div>
<?php endif; ?>
<div class="col-md-4">
<div class="card stat-card">
<div class="d-flex justify-content-between align-items-center">
<div>
<div class="text-muted small">Active Cases</div>
<div class="h3 mb-0"><?= $total_cases ?></div>
</div>
<i class="bi bi-grid stat-icon"></i>
</div>
</div>
</div>
<div class="col-md-4">
<div class="card stat-card">
<div class="d-flex justify-content-between align-items-center">
<div>
<div class="text-muted small">Total Donations</div>
<div class="h3 mb-0">$<?= number_format($total_donations) ?></div>
</div>
<i class="bi bi-cash-stack stat-icon"></i>
</div>
</div>
</div>
</div>
<div class="card p-4">
<h5>Recent Activity</h5>
<p class="text-muted">New donations and case updates will appear here.</p>
<!-- Table of recent donations could go here -->
</div>
</div>
</body>
</html>

99
admin/login.php Normal file
View File

@ -0,0 +1,99 @@
<?php
ob_start();
require_once '../db/config.php';
require_once 'auth.php'; // Use central session logic
$error = '';
$debug = [];
$debug[] = "Method: " . $_SERVER['REQUEST_METHOD'];
$debug[] = "Session ID: " . session_id();
if (isset($_GET['auth_error'])) {
$error = 'Your session has expired or you are not logged in.';
$debug[] = "Redirected from auth.php (Session loss suspected)";
}
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$email = trim($_POST['email'] ?? '');
$password = $_POST['password'] ?? '';
if (empty($email) || empty($password)) {
$error = "Please enter both email and password.";
} else {
try {
$pdo = db();
$stmt = $pdo->prepare("SELECT * FROM users WHERE email = ?");
$stmt->execute([$email]);
$user = $stmt->fetch();
if (!$user) {
$error = 'Email not found.';
$debug[] = "Auth: Email '$email' not in database.";
} elseif (!password_verify($password, $user['password'])) {
$error = 'Incorrect password.';
$debug[] = "Auth: Password mismatch for '$email'.";
} else {
$_SESSION['user_id'] = $user['id'];
$_SESSION['user'] = $user;
// Force session write before redirect
session_write_close();
ob_end_clean();
header('Location: index.php');
exit;
}
} catch (PDOException $e) {
$error = 'Database error: ' . $e->getMessage();
}
}
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Admin Login - CharityHub</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.1/font/bootstrap-icons.css">
<style>
body { background: #f3f4f6; display: flex; align-items: center; justify-content: center; min-height: 100vh; padding: 20px; font-family: sans-serif; }
.login-card { width: 100%; max-width: 400px; padding: 2rem; background: #fff; border-radius: 12px; box-shadow: 0 4px 6px -1px rgba(0,0,0,0.1); }
.btn-success { background-color: #059669; border: none; padding: 0.6rem; }
.debug-box { font-size: 0.75rem; color: #6b7280; background: #f9fafb; padding: 10px; border-radius: 6px; margin-top: 20px; border: 1px solid #e5e7eb; }
</style>
</head>
<body>
<div class="login-card">
<h3 class="text-center mb-4 fw-bold text-success">CharityHub Admin</h3>
<?php if ($error): ?>
<div class="alert alert-danger py-2 small"><?= htmlspecialchars($error) ?></div>
<?php endif; ?>
<form method="POST" action="login.php">
<div class="mb-3">
<label class="form-label small fw-bold">Email</label>
<input type="email" name="email" class="form-control" value="<?= htmlspecialchars($_POST['email'] ?? '') ?>" required autofocus>
</div>
<div class="mb-3">
<label class="form-label small fw-bold">Password</label>
<input type="password" name="password" class="form-control" required>
</div>
<button type="submit" class="btn btn-success w-100 fw-bold">Sign In</button>
</form>
<div class="mt-3 text-center small text-muted">
Test: admin@charityhub.com / admin123
</div>
<div class="debug-box">
<strong>Troubleshooting:</strong><br>
<?php foreach ($debug as $line) echo "" . htmlspecialchars($line) . "<br>"; ?>
Time: <?= date('H:i:s') ?>
</div>
</div>
</body>
</html>
<?php ob_end_flush(); ?>

13
admin/logout.php Normal file
View File

@ -0,0 +1,13 @@
<?php
require_once 'auth.php';
$_SESSION = [];
if (ini_get("session.use_cookies")) {
$params = session_get_cookie_params();
setcookie(session_name(), '', time() - 42000,
$params["path"], $params["domain"],
$params["secure"], $params["httponly"]
);
}
session_destroy();
header('Location: login.php');
exit;

183
admin/organizations.php Normal file
View File

@ -0,0 +1,183 @@
<?php
require_once 'auth.php';
require_once '../db/config.php';
require_login();
if (!is_super_admin()) {
header('Location: index.php');
exit;
}
$pdo = db();
// Handle Delete
if (isset($_GET['delete'])) {
$id = (int)$_GET['delete'];
$pdo->prepare("DELETE FROM organizations WHERE id = ?")->execute([$id]);
header('Location: organizations.php?success=deleted');
exit;
}
// Handle Add/Edit
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$id = $_POST['id'] ?? null;
$name_en = $_POST['name_en'];
$name_ar = $_POST['name_ar'];
$logo_url = $_POST['logo_url'];
if ($id) {
$stmt = $pdo->prepare("UPDATE organizations SET name_en=?, name_ar=?, logo_url=? WHERE id=?");
$stmt->execute([$name_en, $name_ar, $logo_url, $id]);
} else {
$stmt = $pdo->prepare("INSERT INTO organizations (name_en, name_ar, logo_url) VALUES (?, ?, ?)");
$stmt->execute([$name_en, $name_ar, $logo_url]);
}
header('Location: organizations.php?success=saved');
exit;
}
$orgs = $pdo->query("SELECT * FROM organizations")->fetchAll();
$edit_org = null;
if (isset($_GET['edit'])) {
$id = (int)$_GET['edit'];
$stmt = $pdo->prepare("SELECT * FROM organizations WHERE id = ?");
$stmt->execute([$id]);
$edit_org = $stmt->fetch();
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Manage Organizations - CharityHub Admin</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.1/font/bootstrap-icons.css">
<style>
:root { --sidebar-width: 260px; --primary-color: #059669; }
body { background-color: #f3f4f6; }
.sidebar { width: var(--sidebar-width); height: 100vh; position: fixed; left: 0; top: 0; background: #111827; color: #fff; padding: 1.5rem; }
.main-content { margin-left: var(--sidebar-width); padding: 2rem; }
.nav-link { color: #9ca3af; margin-bottom: 0.5rem; border-radius: 8px; }
.nav-link:hover, .nav-link.active { color: #fff; background: #1f2937; }
.nav-link.active { background: var(--primary-color); }
.card { border: none; border-radius: 12px; box-shadow: 0 1px 3px rgba(0,0,0,0.1); }
</style>
</head>
<body>
<div class="sidebar">
<h4 class="mb-4">CharityHub</h4>
<nav class="nav flex-column">
<a href="index.php" class="nav-link"><i class="bi bi-speedometer2 me-2"></i> Dashboard</a>
<a href="organizations.php" class="nav-link active"><i class="bi bi-building me-2"></i> Organizations</a>
<a href="cases.php" class="nav-link"><i class="bi bi-grid me-2"></i> Cases</a>
<a href="donations.php" class="nav-link"><i class="bi bi-cash-stack me-2"></i> Donations</a>
<hr>
<a href="logout.php" class="nav-link"><i class="bi bi-box-arrow-right me-2"></i> Logout</a>
</nav>
</div>
<div class="main-content">
<div class="d-flex justify-content-between align-items-center mb-4">
<h2>Manage Organizations</h2>
<button class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#orgModal" onclick="clearForm()">
<i class="bi bi-plus-lg me-1"></i> New Organization
</button>
</div>
<?php if (isset($_GET['success'])): ?>
<div class="alert alert-success alert-dismissible fade show">
Operation successful!
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
</div>
<?php endif; ?>
<div class="card p-0">
<div class="table-responsive">
<table class="table table-hover mb-0">
<thead class="table-light">
<tr>
<th class="ps-4">Name (EN/AR)</th>
<th>Logo</th>
<th class="text-end pe-4">Actions</th>
</tr>
</thead>
<tbody>
<?php foreach ($orgs as $org): ?>
<tr>
<td class="ps-4">
<div><strong><?= htmlspecialchars($org['name_en']) ?></strong></div>
<small class="text-muted"><?= htmlspecialchars($org['name_ar']) ?></small>
</td>
<td><img src="<?= htmlspecialchars($org['logo_url']) ?>" height="30" alt="logo"></td>
<td class="text-end pe-4">
<a href="?edit=<?= $org['id'] ?>" class="btn btn-sm btn-outline-primary me-1"><i class="bi bi-pencil"></i></a>
<a href="?delete=<?= $org['id'] ?>" class="btn btn-sm btn-outline-danger" onclick="return confirm('Are you sure?')"><i class="bi bi-trash"></i></a>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
</div>
</div>
<!-- Organization Modal -->
<div class="modal fade" id="orgModal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<form method="POST">
<div class="modal-header">
<h5 class="modal-title" id="modalTitle">New Organization</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<input type="hidden" name="id" id="orgId">
<div class="mb-3">
<label class="form-label">Name (English)</label>
<input type="text" name="name_en" id="orgNameEn" class="form-control" required>
</div>
<div class="mb-3">
<label class="form-label">Name (Arabic)</label>
<input type="text" name="name_ar" id="orgNameAr" class="form-control" dir="rtl" required>
</div>
<div class="mb-3">
<label class="form-label">Logo URL</label>
<input type="url" name="logo_url" id="orgLogoUrl" class="form-control">
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
<button type="submit" class="btn btn-primary">Save Organization</button>
</div>
</form>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
<script>
const orgModal = new bootstrap.Modal(document.getElementById('orgModal'));
function clearForm() {
document.getElementById('orgId').value = '';
document.getElementById('orgNameEn').value = '';
document.getElementById('orgNameAr').value = '';
document.getElementById('orgLogoUrl').value = '';
document.getElementById('modalTitle').innerText = 'New Organization';
}
<?php if ($edit_org): ?>
window.addEventListener('load', () => {
document.getElementById('orgId').value = '<?= $edit_org['id'] ?>';
document.getElementById('orgNameEn').value = '<?= addslashes($edit_org['name_en']) ?>';
document.getElementById('orgNameAr').value = '<?= addslashes($edit_org['name_ar']) ?>';
document.getElementById('orgLogoUrl').value = '<?= $edit_org['logo_url'] ?>';
document.getElementById('modalTitle').innerText = 'Edit Organization';
orgModal.show();
});
<?php endif; ?>
</script>
</body>
</html>

9
admin/session_test.php Normal file
View File

@ -0,0 +1,9 @@
<?php
session_start();
if (!isset($_SESSION['test_count'])) {
$_SESSION['test_count'] = 0;
}
$_SESSION['test_count']++;
echo "Session ID: " . session_id() . "<br>";
echo "Count: " . $_SESSION['test_count'] . "<br>";
echo "<a href='session_test.php'>Refresh</a>";

102
checkout.php Normal file
View File

@ -0,0 +1,102 @@
<?php
require_once 'db/config.php';
require_once 'db/thawani_config.php';
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
header('Location: index.php');
exit;
}
$case_id = (int)$_POST['case_id'];
$amount = (float)$_POST['amount'];
$donor_name = $_POST['donor_name'] ?? 'Anonymous';
$donor_email = $_POST['donor_email'] ?? '';
if ($amount <= 0) {
die("Invalid amount");
}
$pdo = db();
// Fetch case details
$stmt = $pdo->prepare("SELECT * FROM cases WHERE id = ?");
$stmt->execute([$case_id]);
$case = $stmt->fetch();
if (!$case) {
die("Case not found");
}
// Create pending donation
$stmt = $pdo->prepare("INSERT INTO donations (case_id, amount, status, donor_name, donor_email) VALUES (?, ?, 'pending', ?, ?)");
$stmt->execute([$case_id, $amount, $donor_name, $donor_email]);
$donation_id = $pdo->lastInsertId();
// Thawani Checkout Session Request
$payload = [
'client_reference_id' => (string)$donation_id,
'products' => [
[
'name' => $case['title_en'],
'unit_amount' => (int)($amount * 1000), // Thawani uses OMR baiza (1 OMR = 1000 baiza)
'quantity' => 1
]
],
'success_url' => 'http://' . $_SERVER['HTTP_HOST'] . '/success.php?session_id={CHECKOUT_SESSION_ID}',
'cancel_url' => 'http://' . $_SERVER['HTTP_HOST'] . '/index.php',
'metadata' => [
'donation_id' => $donation_id,
'case_id' => $case_id
]
];
// In a real scenario, we'd use CURL to call Thawani API.
// Since we don't have real keys, we'll mock the redirect or show a simulation.
if (THAWANI_SECRET_KEY === 'rRQ26GcsZ60u9Y9v9876543210') {
// Simulation Mode
?>
<!DOCTYPE html>
<html>
<head><title>Simulating Thawani Checkout</title><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css"></head>
<body class="bg-light p-5 text-center">
<div class="card mx-auto" style="max-width: 500px;">
<div class="card-body">
<img src="https://checkout.thawani.om/logo.png" alt="Thawani" height="50" class="mb-4">
<h3>Thawani Checkout Simulation</h3>
<p>Donation ID: #<?= $donation_id ?></p>
<p>Amount: OMR <?= number_format($amount, 3) ?></p>
<div class="alert alert-info small">This is a simulation because no valid Thawani keys are configured in <code>db/thawani_config.php</code>.</div>
<form action="success.php" method="GET">
<input type="hidden" name="session_id" value="mock_session_<?= time() ?>">
<input type="hidden" name="donation_id" value="<?= $donation_id ?>">
<button type="submit" class="btn btn-success w-100">Simulate Success Payment</button>
</form>
<a href="index.php" class="btn btn-link mt-2">Cancel</a>
</div>
</div>
</body>
</html>
<?php
exit;
}
// REAL CURL CALL (if keys were valid)
/*
$ch = curl_init(THAWANI_API_URL . '/checkout/session');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload));
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Content-Type: application/json',
'Thawani-Api-Key: ' . THAWANI_SECRET_KEY
]);
$response = curl_exec($ch);
$data = json_decode($response, true);
if (isset($data['data']['session_id'])) {
$session_id = $data['data']['session_id'];
header("Location: https://checkout.thawani.om/pay/" . $session_id . "?key=" . THAWANI_PUBLISHABLE_KEY);
} else {
echo "Thawani Error: " . ($data['description'] ?? 'Unknown error');
}
*/

12
db/thawani_config.php Normal file
View File

@ -0,0 +1,12 @@
<?php
// Thawani Gateway Configuration
// Replace with real keys from Thawani Merchant Dashboard
define('THAWANI_SECRET_KEY', getenv('THAWANI_SECRET_KEY') ?: 'rRQ26GcsZ60u9Y9v9876543210'); // Placeholder
define('THAWANI_PUBLISHABLE_KEY', getenv('THAWANI_PUBLISHABLE_KEY') ?: 'HGvT9876543210'); // Placeholder
define('THAWANI_ENV', 'sandbox'); // sandbox or production
$thawani_url = (THAWANI_ENV === 'sandbox')
? 'https://uatapi.thawani.om/api/v1'
: 'https://api.thawani.om/api/v1';
define('THAWANI_API_URL', $thawani_url);

537
index.php
View File

@ -1,150 +1,405 @@
<?php
declare(strict_types=1);
@ini_set('display_errors', '1');
@error_reporting(E_ALL);
@date_default_timezone_set('UTC');
require_once 'db/config.php';
$phpVersion = PHP_VERSION;
$now = date('Y-m-d H:i:s');
?>
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>New Style</title>
<?php
// Read project preview data from environment
$projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? '';
// Language configuration
$lang = $_GET['lang'] ?? 'en';
if (!in_array($lang, ['en', 'ar'])) $lang = 'en';
$dir = ($lang === 'ar') ? 'rtl' : 'ltr';
// UI Text
$texts = [
'en' => [
'title' => 'Support a Cause',
'subtitle' => 'Empower communities worldwide through your generosity.',
'all_orgs' => 'All Organizations',
'raised' => 'Raised',
'of' => 'of',
'goal' => 'Goal',
'donate_now' => 'Donate Now',
'lang_name' => 'العربية',
'lang_code' => 'ar',
'hero_title' => 'Make a Real Impact Today',
'hero_sub' => 'Choose a campaign from our trusted partner organizations and help change lives in minutes.',
'no_cases' => 'No active cases found for this organization.',
'admin_panel' => 'Admin Panel',
'modal_title' => 'Make a Donation',
'modal_amount' => 'Amount (OMR)',
'modal_name' => 'Your Name',
'modal_email' => 'Your Email',
'modal_submit' => 'Proceed to Payment',
],
'ar' => [
'title' => 'ادعم قضية',
'subtitle' => 'مكن المجتمعات في جميع أنحاء العالم من خلال كرمك.',
'all_orgs' => 'جميع المؤسسات',
'raised' => 'تم جمع',
'of' => 'من',
'goal' => 'الهدف',
'donate_now' => 'تبرع الآن',
'lang_name' => 'English',
'lang_code' => 'en',
'hero_title' => 'أحدث تأثيراً حقيقياً اليوم',
'hero_sub' => 'اختر حملة من مؤسساتنا الشريكة الموثوقة وساعد في تغيير الأرواح في دقائق.',
'no_cases' => 'لا توجد حالات نشطة لهذه المؤسسة.',
'admin_panel' => 'لوحة التحكم',
'modal_title' => 'تبرع الآن',
'modal_amount' => 'المبلغ (ريال عماني)',
'modal_name' => 'الاسم',
'modal_email' => 'البريد الإلكتروني',
'modal_submit' => 'الانتقال للدفع',
]
];
$t = $texts[$lang];
// Database fetch
$pdo = db();
$orgs = $pdo->query("SELECT * FROM organizations")->fetchAll();
$selected_org = $_GET['org'] ?? 'all';
$sql = "SELECT c.*, o.name_en as org_name_en, o.name_ar as org_name_ar
FROM cases c
JOIN organizations o ON c.org_id = o.id";
if ($selected_org !== 'all') {
$sql .= " WHERE c.org_id = " . (int)$selected_org;
}
$cases = $pdo->query($sql)->fetchAll();
// Project meta
$projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? $t['subtitle'];
$projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
?>
<?php if ($projectDescription): ?>
<!-- Meta description -->
<meta name="description" content='<?= htmlspecialchars($projectDescription) ?>' />
<!-- Open Graph meta tags -->
<meta property="og:description" content="<?= htmlspecialchars($projectDescription) ?>" />
<!-- Twitter meta tags -->
<meta property="twitter:description" content="<?= htmlspecialchars($projectDescription) ?>" />
<?php endif; ?>
<?php if ($projectImageUrl): ?>
<!-- Open Graph image -->
<meta property="og:image" content="<?= htmlspecialchars($projectImageUrl) ?>" />
<!-- Twitter image -->
<meta property="twitter:image" content="<?= htmlspecialchars($projectImageUrl) ?>" />
<?php endif; ?>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;700&display=swap" rel="stylesheet">
<style>
:root {
--bg-color-start: #6a11cb;
--bg-color-end: #2575fc;
--text-color: #ffffff;
--card-bg-color: rgba(255, 255, 255, 0.01);
--card-border-color: rgba(255, 255, 255, 0.1);
}
body {
margin: 0;
font-family: 'Inter', sans-serif;
background: linear-gradient(45deg, var(--bg-color-start), var(--bg-color-end));
color: var(--text-color);
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
text-align: center;
overflow: hidden;
position: relative;
}
body::before {
content: '';
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-image: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100" viewBox="0 0 100 100"><path d="M-10 10L110 10M10 -10L10 110" stroke-width="1" stroke="rgba(255,255,255,0.05)"/></svg>');
animation: bg-pan 20s linear infinite;
z-index: -1;
}
@keyframes bg-pan {
0% { background-position: 0% 0%; }
100% { background-position: 100% 100%; }
}
main {
padding: 2rem;
}
.card {
background: var(--card-bg-color);
border: 1px solid var(--card-border-color);
border-radius: 16px;
padding: 2rem;
backdrop-filter: blur(20px);
-webkit-backdrop-filter: blur(20px);
box-shadow: 0 8px 32px 0 rgba(0, 0, 0, 0.1);
}
.loader {
margin: 1.25rem auto 1.25rem;
width: 48px;
height: 48px;
border: 3px solid rgba(255, 255, 255, 0.25);
border-top-color: #fff;
border-radius: 50%;
animation: spin 1s linear infinite;
}
@keyframes spin {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
.hint {
opacity: 0.9;
}
.sr-only {
position: absolute;
width: 1px; height: 1px;
padding: 0; margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap; border: 0;
}
h1 {
font-size: 3rem;
font-weight: 700;
margin: 0 0 1rem;
letter-spacing: -1px;
}
p {
margin: 0.5rem 0;
font-size: 1.1rem;
}
code {
background: rgba(0,0,0,0.2);
padding: 2px 6px;
border-radius: 4px;
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
}
footer {
position: absolute;
bottom: 1rem;
font-size: 0.8rem;
opacity: 0.7;
}
</style>
<!DOCTYPE html>
<html lang="<?= $lang ?>" dir="<?= $dir ?>">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title><?= $t['title'] ?></title>
<!-- Meta Tags -->
<meta name="description" content="<?= htmlspecialchars($projectDescription) ?>">
<?php if ($projectImageUrl): ?>
<meta property="og:image" content="<?= htmlspecialchars($projectImageUrl) ?>">
<?php endif; ?>
<!-- Fonts & CSS -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<?php if ($lang === 'en'): ?>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
<?php else: ?>
<link href="https://fonts.googleapis.com/css2?family=Cairo:wght@400;500;600;700&display=swap" rel="stylesheet">
<?php endif; ?>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css">
<?php if ($lang === 'ar'): ?>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.rtl.min.css">
<?php endif; ?>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.1/font/bootstrap-icons.css">
<style>
:root {
--primary-color: #059669;
--primary-hover: #047857;
--bg-light: #f9fafb;
--text-main: #111827;
--text-muted: #6b7280;
--font-family: <?= ($lang === 'ar') ? "'Cairo', sans-serif" : "'Inter', sans-serif" ?>;
}
body {
font-family: var(--font-family);
background-color: var(--bg-light);
color: var(--text-main);
margin: 0;
padding: 0;
}
.navbar {
background: #fff;
border-bottom: 1px solid #e5e7eb;
padding: 1rem 0;
}
.brand-text {
font-weight: 700;
font-size: 1.5rem;
color: var(--primary-color);
text-decoration: none;
}
.hero {
padding: 5rem 0;
background: linear-gradient(rgba(255,255,255,0.9), rgba(255,255,255,0.9)), url('https://images.pexels.com/photos/6646917/pexels-photo-6646917.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1');
background-size: cover;
background-position: center;
text-align: center;
margin-bottom: 3rem;
}
.hero h1 {
font-size: 3rem;
font-weight: 800;
margin-bottom: 1.5rem;
letter-spacing: -0.025em;
}
.hero p {
font-size: 1.25rem;
color: var(--text-muted);
max-width: 700px;
margin: 0 auto;
}
.org-tabs {
margin-bottom: 2rem;
display: flex;
gap: 1rem;
overflow-x: auto;
padding-bottom: 1rem;
}
.org-tab {
padding: 0.5rem 1.5rem;
border-radius: 9999px;
background: #fff;
border: 1px solid #e5e7eb;
color: var(--text-muted);
text-decoration: none;
white-space: nowrap;
transition: all 0.2s;
}
.org-tab:hover, .org-tab.active {
background: var(--primary-color);
color: #fff;
border-color: var(--primary-color);
}
.case-card {
background: #fff;
border-radius: 12px;
overflow: hidden;
border: 1px solid #e5e7eb;
transition: transform 0.2s, box-shadow 0.2s;
height: 100%;
display: flex;
flex-direction: column;
}
.case-card:hover {
transform: translateY(-4px);
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1);
}
.case-image {
height: 200px;
width: 100%;
object-fit: cover;
}
.case-content {
padding: 1.5rem;
flex-grow: 1;
display: flex;
flex-direction: column;
}
.case-org {
font-size: 0.875rem;
color: var(--primary-color);
font-weight: 600;
margin-bottom: 0.5rem;
}
.case-title {
font-size: 1.25rem;
font-weight: 700;
margin-bottom: 1rem;
color: var(--text-main);
}
.progress-container {
margin: 1.5rem 0;
}
.progress {
height: 10px;
border-radius: 9999px;
background-color: #e5e7eb;
margin-bottom: 0.5rem;
}
.progress-bar {
background-color: var(--primary-color);
}
.progress-stats {
display: flex;
justify-content: space-between;
font-size: 0.875rem;
color: var(--text-muted);
}
.btn-donate {
background-color: var(--primary-color);
color: #fff;
border: none;
padding: 0.75rem 1.5rem;
border-radius: 8px;
font-weight: 600;
width: 100%;
transition: background 0.2s;
}
.btn-donate:hover {
background-color: var(--primary-hover);
color: #fff;
}
footer {
background: #fff;
border-top: 1px solid #e5e7eb;
padding: 3rem 0;
margin-top: 5rem;
text-align: center;
}
/* RTL Adjustments */
[dir="rtl"] .hero h1 {
letter-spacing: 0;
}
</style>
</head>
<body>
<main>
<div class="card">
<h1>Analyzing your requirements and generating your website…</h1>
<div class="loader" role="status" aria-live="polite" aria-label="Applying initial changes">
<span class="sr-only">Loading…</span>
</div>
<p class="hint"><?= ($_SERVER['HTTP_HOST'] ?? '') === 'appwizzy.com' ? 'AppWizzy' : 'Flatlogic' ?> AI is collecting your requirements and applying the first changes.</p>
<p class="hint">This page will update automatically as the plan is implemented.</p>
<p>Runtime: PHP <code><?= htmlspecialchars($phpVersion) ?></code> — UTC <code><?= htmlspecialchars($now) ?></code></p>
<nav class="navbar sticky-top">
<div class="container">
<a class="brand-text" href="index.php?lang=<?= $lang ?>">
<i class="bi bi-heart-fill me-2"></i>CharityHub
</a>
<div class="d-flex align-items-center gap-3">
<a href="admin/" class="text-decoration-none text-muted small"><i class="bi bi-person-lock me-1"></i><?= $t['admin_panel'] ?></a>
<a href="?lang=<?= $t['lang_code'] ?>" class="btn btn-outline-secondary btn-sm rounded-pill">
<i class="bi bi-translate me-1"></i><?= $t['lang_name'] ?>
</a>
</div>
</div>
</main>
<footer>
Page updated: <?= htmlspecialchars($now) ?> (UTC)
</footer>
</nav>
<header class="hero">
<div class="container">
<h1><?= $t['hero_title'] ?></h1>
<p><?= $t['hero_sub'] ?></p>
</div>
</header>
<main class="container">
<div class="org-tabs">
<a href="?lang=<?= $lang ?>&org=all" class="org-tab <?= $selected_org === 'all' ? 'active' : '' ?>">
<?= $t['all_orgs'] ?>
</a>
<?php foreach ($orgs as $org): ?>
<a href="?lang=<?= $lang ?>&org=<?= $org['id'] ?>" class="org-tab <?= $selected_org == $org['id'] ? 'active' : '' ?>">
<?= $lang === 'en' ? $org['name_en'] : $org['name_ar'] ?>
</a>
<?php endforeach; ?>
</div>
<div class="row g-4">
<?php if (empty($cases)): ?>
<div class="col-12 text-center py-5">
<i class="bi bi-search d-block mb-3 fs-1 text-muted"></i>
<p class="text-muted"><?= $t['no_cases'] ?></p>
</div>
<?php endif; ?>
<?php foreach ($cases as $case): ?>
<?php
$pct = min(100, round(($case['raised'] / $case['goal']) * 100));
?>
<div class="col-md-6 col-lg-4">
<div class="case-card">
<img src="<?= $case['image_url'] ?>" class="case-image" alt="Case Image">
<div class="case-content">
<span class="case-org"><?= $lang === 'en' ? $case['org_name_en'] : $case['org_name_ar'] ?></span>
<h3 class="case-title"><?= $lang === 'en' ? $case['title_en'] : $case['title_ar'] ?></h3>
<p class="text-muted flex-grow-1 small"><?= $lang === 'en' ? $case['desc_en'] : $case['desc_ar'] ?></p>
<div class="progress-container">
<div class="progress">
<div class="progress-bar" role="progressbar" style="width: <?= $pct ?>%" aria-valuenow="<?= $pct ?>" aria-valuemin="0" aria-valuemax="100"></div>
</div>
<div class="progress-stats">
<span><strong><?= $t['raised'] ?>:</strong> OMR <?= number_format($case['raised']) ?></span>
<span><strong><?= $t['goal'] ?>:</strong> OMR <?= number_format($case['goal']) ?></span>
</div>
</div>
<button class="btn btn-donate" data-bs-toggle="modal" data-bs-target="#donateModal"
data-case-id="<?= $case['id'] ?>"
data-case-title="<?= htmlspecialchars($lang === 'en' ? $case['title_en'] : $case['title_ar']) ?>">
<?= $t['donate_now'] ?>
</button>
</div>
</div>
</div>
<?php endforeach; ?>
</div>
</main>
<!-- Donate Modal -->
<div class="modal fade" id="donateModal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<form action="checkout.php" method="POST">
<div class="modal-header">
<h5 class="modal-title"><?= $t['modal_title'] ?></h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<p id="modalCaseTitle" class="fw-bold text-success"></p>
<input type="hidden" name="case_id" id="modalCaseId">
<div class="mb-3">
<label class="form-label"><?= $t['modal_amount'] ?></label>
<input type="number" name="amount" class="form-control form-control-lg" step="0.100" min="1" placeholder="5.000" required>
</div>
<div class="mb-3">
<label class="form-label"><?= $t['modal_name'] ?></label>
<input type="text" name="donor_name" class="form-control" placeholder="John Doe">
</div>
<div class="mb-3">
<label class="form-label"><?= $t['modal_email'] ?></label>
<input type="email" name="donor_email" class="form-control" placeholder="john@example.com">
</div>
</div>
<div class="modal-footer">
<button type="submit" class="btn btn-success w-100 py-2 fw-bold"><?= $t['modal_submit'] ?></button>
</div>
</form>
</div>
</div>
</div>
<footer>
<div class="container">
<p class="mb-0 text-muted">&copy; <?= date('Y') ?> CharityHub. All rights reserved.</p>
</div>
</footer>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
<script>
const donateModal = document.getElementById('donateModal');
if (donateModal) {
donateModal.addEventListener('show.bs.modal', event => {
const button = event.relatedTarget;
const caseId = button.getAttribute('data-case-id');
const caseTitle = button.getAttribute('data-case-title');
donateModal.querySelector('#modalCaseId').value = caseId;
donateModal.querySelector('#modalCaseTitle').textContent = caseTitle;
});
}
</script>
</body>
</html>

67
success.php Normal file
View File

@ -0,0 +1,67 @@
<?php
require_once 'db/config.php';
$session_id = $_GET['session_id'] ?? null;
$donation_id = $_GET['donation_id'] ?? null; // For simulation
if (!$session_id) {
header('Location: index.php');
exit;
}
$pdo = db();
// In simulation we use donation_id from URL
// In real life, we'd verify session_id with Thawani API
if (strpos($session_id, 'mock_session_') === 0 && $donation_id) {
$stmt = $pdo->prepare("SELECT * FROM donations WHERE id = ? AND status = 'pending'");
$stmt->execute([$donation_id]);
$donation = $stmt->fetch();
} else {
// Real Thawani verification logic would go here
// $donation = ... fetch by session_id/client_reference_id
}
if ($donation) {
// Update donation status
$pdo->prepare("UPDATE donations SET status = 'completed', transaction_id = ? WHERE id = ?")
->execute([$session_id, $donation['id']]);
// Update case raised amount
$pdo->prepare("UPDATE cases SET raised = raised + ? WHERE id = ?")
->execute([$donation['amount'], $donation['case_id']]);
$success = true;
} else {
$success = false;
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Donation Successful - CharityHub</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css">
</head>
<body class="bg-light">
<div class="container py-5 text-center">
<?php if ($success): ?>
<div class="card mx-auto p-5 shadow-sm" style="max-width: 600px; border-radius: 20px;">
<div class="text-success mb-4">
<svg xmlns="http://www.w3.org/2000/svg" width="80" height="80" fill="currentColor" class="bi bi-check-circle-fill" viewBox="0 0 16 16">
<path d="M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0zm-3.97-3.03a.75.75 0 0 0-1.08.022L7.477 9.417 5.384 7.323a.75.75 0 0 0-1.06 1.06L6.97 11.03a.75.75 0 0 0 1.079-.02l3.992-4.99a.75.75 0 0 0-.01-1.05z"/>
</svg>
</div>
<h2>Thank You!</h2>
<p class="lead text-muted">Your donation has been successfully processed. You have made a real difference today.</p>
<hr class="my-4">
<a href="index.php" class="btn btn-primary px-5 py-2 rounded-pill" style="background-color: #059669; border: none;">Back to Home</a>
</div>
<?php else: ?>
<div class="alert alert-danger">Something went wrong or the donation was already processed.</div>
<a href="index.php" class="btn btn-secondary">Back to Home</a>
<?php endif; ?>
</div>
</body>
</html>