Autosave: 20260213-074012

This commit is contained in:
Flatlogic Bot 2026-02-13 07:40:13 +00:00
parent 8b05731720
commit cfd8ea5733
13 changed files with 778 additions and 307 deletions

View File

@ -3,50 +3,55 @@ 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']]);
}
$pdo->prepare("DELETE FROM cases WHERE id = ?")->execute([$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
}
$cases = $pdo->query("SELECT c.*, cat.name_en as cat_name FROM cases c LEFT JOIN categories cat ON c.category_id = cat.id ORDER BY c.id DESC")->fetchAll();
$categories = $pdo->query("SELECT * FROM categories")->fetchAll();
// Handle Add/Edit
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$id = $_POST['id'] ?? null;
$org_id = is_super_admin() ? $_POST['org_id'] : $user['org_id'];
$category_id = $_POST['category_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'];
$importance = $_POST['importance'] ?? 'normal';
$status = $_POST['status'] ?? 'active';
$image_url = $_POST['image_url_existing'] ?? '';
// Handle File Upload
if (isset($_FILES['image']) && $_FILES['image']['error'] === UPLOAD_ERR_OK) {
$ext = strtolower(pathinfo($_FILES['image']['name'], PATHINFO_EXTENSION));
$allowed = ['jpg', 'jpeg', 'png', 'webp', 'gif'];
if (in_array($ext, $allowed)) {
$filename = uniqid('case_') . '.' . $ext;
$upload_dir = '../assets/images/cases/';
if (!is_dir($upload_dir)) mkdir($upload_dir, 0775, true);
if (move_uploaded_file($_FILES['image']['tmp_name'], $upload_dir . $filename)) {
$image_url = 'assets/images/cases/' . $filename;
}
}
}
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]);
$stmt = $pdo->prepare("UPDATE cases SET category_id=?, title_en=?, title_ar=?, desc_en=?, desc_ar=?, goal=?, image_url=?, importance=?, status=? WHERE id=?");
$stmt->execute([$category_id, $title_en, $title_ar, $desc_en, $desc_ar, $goal, $image_url, $importance, $status, $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]);
$stmt = $pdo->prepare("INSERT INTO cases (category_id, title_en, title_ar, desc_en, desc_ar, goal, raised, image_url, importance, status) VALUES (?, ?, ?, ?, ?, ?, 0, ?, ?, ?)");
$stmt->execute([$category_id, $title_en, $title_ar, $desc_en, $desc_ar, $goal, $image_url, $importance, $status]);
}
header('Location: cases.php?success=saved');
exit;
@ -55,13 +60,8 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$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']]);
}
$stmt = $pdo->prepare("SELECT * FROM cases WHERE id = ?");
$stmt->execute([$id]);
$edit_case = $stmt->fetch();
}
?>
@ -82,22 +82,14 @@ if (isset($_GET['edit'])) {
.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-urgent { background-color: #ef4444; }
.badge-top_priority { background-color: #8b5cf6; }
.badge-normal { background-color: #6b7280; }
.case-img-preview { width: 50px; height: 50px; object-fit: cover; border-radius: 6px; }
</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>
<?php include "sidebar.php"; ?>
<div class="main-content">
<div class="d-flex justify-content-between align-items-center mb-4">
@ -119,10 +111,11 @@ if (isset($_GET['edit'])) {
<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 class="ps-4">Case</th>
<th>Category</th>
<th>Goal</th>
<th>Raised</th>
<th>Status</th>
<th>Importance</th>
<th class="text-end pe-4">Actions</th>
</tr>
</thead>
@ -130,12 +123,39 @@ if (isset($_GET['edit'])) {
<?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>
<div class="d-flex align-items-center">
<?php if ($case['image_url']): ?>
<img src="../<?= htmlspecialchars($case['image_url']) ?>" class="case-img-preview me-3" alt="">
<?php else: ?>
<div class="case-img-preview me-3 bg-secondary d-flex align-items-center justify-content-center text-white">
<i class="bi bi-image"></i>
</div>
<?php endif; ?>
<div>
<div><strong><?= htmlspecialchars($case['title_en']) ?></strong></div>
<small class="text-muted"><?= htmlspecialchars($case['title_ar']) ?></small>
</div>
</div>
</td>
<td><?= htmlspecialchars($case['cat_name'] ?? 'N/A') ?></td>
<td>
<div>$<?= number_format($case['goal']) ?></div>
<small class="text-muted">Raised: $<?= number_format($case['raised']) ?></small>
</td>
<td>
<?php if ($case['status'] === 'active'): ?>
<span class="badge bg-success">Active</span>
<?php elseif ($case['status'] === 'paused'): ?>
<span class="badge bg-warning text-dark">Paused</span>
<?php else: ?>
<span class="badge bg-danger">Disabled</span>
<?php endif; ?>
</td>
<td>
<span class="badge badge-<?= $case['importance'] ?>">
<?= ucfirst(str_replace('_', ' ', $case['importance'])) ?>
</span>
</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>
@ -152,23 +172,25 @@ if (isset($_GET['edit'])) {
<div class="modal fade" id="caseModal" tabindex="-1">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<form method="POST">
<form method="POST" enctype="multipart/form-data">
<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()): ?>
<input type="hidden" name="image_url_existing" id="caseImageUrlExisting">
<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>
<label class="form-label">Category</label>
<select name="category_id" id="caseCategory" class="form-select" required>
<option value="">Select Category</option>
<?php foreach ($categories as $cat): ?>
<option value="<?= $cat['id'] ?>"><?= htmlspecialchars($cat['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>
@ -179,6 +201,7 @@ if (isset($_GET['edit'])) {
<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>
@ -189,14 +212,36 @@ if (isset($_GET['edit'])) {
<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">
<div class="col-md-4 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 class="col-md-4 mb-3">
<label class="form-label">Importance</label>
<select name="importance" id="caseImportance" class="form-select">
<option value="normal">Normal</option>
<option value="urgent">Urgent</option>
<option value="top_priority">Top Priority</option>
</select>
</div>
<div class="col-md-4 mb-3">
<label class="form-label">Status</label>
<select name="status" id="caseStatus" class="form-select">
<option value="active">Active</option>
<option value="paused">Paused</option>
<option value="disabled">Disabled</option>
</select>
</div>
</div>
<div class="mb-3">
<label class="form-label">Case Image</label>
<input type="file" name="image" class="form-control" accept="image/*">
<div id="imagePreviewContainer" class="mt-2" style="display:none;">
<small class="text-muted d-block mb-1">Current Image:</small>
<img id="imagePreview" src="" class="rounded border" style="max-height: 100px;">
</div>
</div>
</div>
@ -215,27 +260,37 @@ if (isset($_GET['edit'])) {
function clearForm() {
document.getElementById('caseId').value = '';
document.getElementById('caseCategory').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('caseImportance').value = 'normal';
document.getElementById('caseStatus').value = 'active';
document.getElementById('caseImageUrlExisting').value = '';
document.getElementById('imagePreviewContainer').style.display = 'none';
document.getElementById('modalTitle').innerText = 'New Case';
}
<?php if ($edit_case): ?>
window.addEventListener('load', () => {
document.getElementById('caseId').value = '<?= $edit_case['id'] ?>';
document.getElementById('caseCategory').value = '<?= $edit_case['category_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('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('caseImportance').value = '<?= $edit_case['importance'] ?>';
document.getElementById('caseStatus').value = '<?= $edit_case['status'] ?>';
document.getElementById('caseImageUrlExisting').value = '<?= $edit_case['image_url'] ?>';
if ('<?= $edit_case['image_url'] ?>') {
document.getElementById('imagePreview').src = '../<?= $edit_case['image_url'] ?>';
document.getElementById('imagePreviewContainer').style.display = 'block';
}
document.getElementById('modalTitle').innerText = 'Edit Case';
caseModal.show();
});

View File

@ -13,8 +13,8 @@ $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');
$pdo->prepare("DELETE FROM categories WHERE id = ?")->execute([$id]);
header('Location: categories.php?success=deleted');
exit;
}
@ -23,27 +23,26 @@ 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]);
$stmt = $pdo->prepare("UPDATE categories SET name_en=?, name_ar=? WHERE id=?");
$stmt->execute([$name_en, $name_ar, $id]);
} else {
$stmt = $pdo->prepare("INSERT INTO organizations (name_en, name_ar, logo_url) VALUES (?, ?, ?)");
$stmt->execute([$name_en, $name_ar, $logo_url]);
$stmt = $pdo->prepare("INSERT INTO categories (name_en, name_ar) VALUES (?, ?)");
$stmt->execute([$name_en, $name_ar]);
}
header('Location: organizations.php?success=saved');
header('Location: categories.php?success=saved');
exit;
}
$orgs = $pdo->query("SELECT * FROM organizations")->fetchAll();
$categories = $pdo->query("SELECT * FROM categories")->fetchAll();
$edit_org = null;
$edit_cat = null;
if (isset($_GET['edit'])) {
$id = (int)$_GET['edit'];
$stmt = $pdo->prepare("SELECT * FROM organizations WHERE id = ?");
$stmt = $pdo->prepare("SELECT * FROM categories WHERE id = ?");
$stmt->execute([$id]);
$edit_org = $stmt->fetch();
$edit_cat = $stmt->fetch();
}
?>
<!DOCTYPE html>
@ -51,7 +50,7 @@ if (isset($_GET['edit'])) {
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Manage Organizations - CharityHub Admin</title>
<title>Manage Categories - 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>
@ -66,23 +65,13 @@ if (isset($_GET['edit'])) {
</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>
<?php include "sidebar.php"; ?>
<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
<h2>Manage Categories</h2>
<button class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#catModal" onclick="clearForm()">
<i class="bi bi-plus-lg me-1"></i> New Category
</button>
</div>
@ -99,21 +88,19 @@ if (isset($_GET['edit'])) {
<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): ?>
<?php foreach ($categories as $cat): ?>
<tr>
<td class="ps-4">
<div><strong><?= htmlspecialchars($org['name_en']) ?></strong></div>
<small class="text-muted"><?= htmlspecialchars($org['name_ar']) ?></small>
<div><strong><?= htmlspecialchars($cat['name_en']) ?></strong></div>
<small class="text-muted"><?= htmlspecialchars($cat['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>
<a href="?edit=<?= $cat['id'] ?>" class="btn btn-sm btn-outline-primary me-1"><i class="bi bi-pencil"></i></a>
<a href="?delete=<?= $cat['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; ?>
@ -123,33 +110,29 @@ if (isset($_GET['edit'])) {
</div>
</div>
<!-- Organization Modal -->
<div class="modal fade" id="orgModal" tabindex="-1">
<!-- Category Modal -->
<div class="modal fade" id="catModal" 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>
<h5 class="modal-title" id="modalTitle">New Category</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<input type="hidden" name="id" id="orgId">
<input type="hidden" name="id" id="catId">
<div class="mb-3">
<label class="form-label">Name (English)</label>
<input type="text" name="name_en" id="orgNameEn" class="form-control" required>
<input type="text" name="name_en" id="catNameEn" 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">
<input type="text" name="name_ar" id="catNameAr" class="form-control" dir="rtl" required>
</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>
<button type="submit" class="btn btn-primary">Save Category</button>
</div>
</form>
</div>
@ -158,26 +141,24 @@ if (isset($_GET['edit'])) {
<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'));
const catModal = new bootstrap.Modal(document.getElementById('catModal'));
function clearForm() {
document.getElementById('orgId').value = '';
document.getElementById('orgNameEn').value = '';
document.getElementById('orgNameAr').value = '';
document.getElementById('orgLogoUrl').value = '';
document.getElementById('modalTitle').innerText = 'New Organization';
document.getElementById('catId').value = '';
document.getElementById('catNameEn').value = '';
document.getElementById('catNameAr').value = '';
document.getElementById('modalTitle').innerText = 'New Category';
}
<?php if ($edit_org): ?>
<?php if ($edit_cat): ?>
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();
document.getElementById('catId').value = '<?= $edit_cat['id'] ?>';
document.getElementById('catNameEn').value = '<?= addslashes($edit_cat['name_en']) ?>';
document.getElementById('catNameAr').value = '<?= addslashes($edit_cat['name_ar']) ?>';
document.getElementById('modalTitle').innerText = 'Edit Category';
catModal.show();
});
<?php endif; ?>
</script>
</body>
</html>
</html>

View File

@ -7,22 +7,11 @@ $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();
}
$donations = $pdo->query("SELECT d.*, c.title_en as case_title, cat.name_en as cat_name
FROM donations d
JOIN cases c ON d.case_id = c.id
LEFT JOIN categories cat ON c.category_id = cat.id
ORDER BY d.created_at DESC")->fetchAll();
?>
<!DOCTYPE html>
<html lang="en">
@ -47,19 +36,7 @@ if (is_super_admin()) {
</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>
<?php include "sidebar.php"; ?>
<div class="main-content">
<div class="d-flex justify-content-between align-items-center mb-4">
@ -72,8 +49,8 @@ if (is_super_admin()) {
<thead class="table-light">
<tr>
<th class="ps-4">Donor</th>
<th>Contact</th>
<th>Case</th>
<?php if (is_super_admin()): ?><th>Organization</th><?php endif; ?>
<th>Amount</th>
<th>Status</th>
<th>Date</th>
@ -83,11 +60,16 @@ if (is_super_admin()) {
<?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>
<strong><?= htmlspecialchars($don['donor_name'] ?: 'Anonymous') ?></strong>
</td>
<td>
<div class="small"><?= htmlspecialchars($don['donor_email']) ?></div>
<div class="small text-muted"><?= htmlspecialchars($don['donor_phone'] ?? 'N/A') ?></div>
</td>
<td>
<div class="small fw-bold"><?= htmlspecialchars($don['case_title']) ?></div>
<div class="small text-muted"><?= htmlspecialchars($don['cat_name'] ?? 'General') ?></div>
</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'] ?>">
@ -99,7 +81,7 @@ if (is_super_admin()) {
<?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>
<td colspan="6" class="text-center py-4 text-muted">No donations found.</td>
</tr>
<?php endif; ?>
</tbody>

View File

@ -7,39 +7,18 @@ $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;
// Fetch recent donations
$recent_donations = $pdo->query("
SELECT d.*, c.title_en as case_title
FROM donations d
JOIN cases c ON d.case_id = c.id
ORDER BY d.created_at DESC
LIMIT 5
")->fetchAll();
} 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;
// Fetch recent donations for this org
$recent_donations = $pdo->query("
SELECT d.*, c.title_en as case_title
FROM donations d
JOIN cases c ON d.case_id = c.id
WHERE c.org_id = $org_id
ORDER BY d.created_at DESC
LIMIT 5
")->fetchAll();
}
$total_categories = $pdo->query("SELECT COUNT(*) FROM categories")->fetchColumn();
$total_cases = $pdo->query("SELECT COUNT(*) FROM cases")->fetchColumn();
$total_donations = $pdo->query("SELECT SUM(amount) FROM donations WHERE status = 'completed'")->fetchColumn() ?: 0;
// Fetch recent donations
$recent_donations = $pdo->query("
SELECT d.*, c.title_en as case_title
FROM donations d
JOIN cases c ON d.case_id = c.id
ORDER BY d.created_at DESC
LIMIT 5
")->fetchAll();
?>
<!DOCTYPE html>
<html lang="en">
@ -66,44 +45,30 @@ if (is_super_admin()) {
</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>
<?php include "sidebar.php"; ?>
<div class="main-content">
<div class="d-flex justify-content-between align-items-center mb-4">
<div>
<h2 class="mb-0">Welcome, <?= is_super_admin() ? 'Super Admin' : htmlspecialchars($user['name'] ?? 'Org Admin') ?></h2>
<h2 class="mb-0">Welcome, <?= htmlspecialchars($user['name'] ?? 'Admin') ?></h2>
<p class="text-muted mb-0">Manage your charity activities and donations.</p>
</div>
<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 class="text-muted small">Categories</div>
<div class="h3 mb-0"><?= $total_categories ?></div>
</div>
<i class="bi bi-building stat-icon"></i>
<i class="bi bi-tags stat-icon"></i>
</div>
</div>
</div>
<?php endif; ?>
<div class="col-md-<?= is_super_admin() ? '4' : '6' ?>">
<div class="col-md-4">
<div class="card stat-card">
<div class="d-flex justify-content-between align-items-center">
<div>
@ -114,7 +79,7 @@ if (is_super_admin()) {
</div>
</div>
</div>
<div class="col-md-<?= is_super_admin() ? '4' : '6' ?>">
<div class="col-md-4">
<div class="card stat-card">
<div class="d-flex justify-content-between align-items-center">
<div>
@ -172,4 +137,4 @@ if (is_super_admin()) {
</div>
</div>
</body>
</html>
</html>

147
admin/profile.php Normal file
View File

@ -0,0 +1,147 @@
<?php
require_once 'auth.php';
require_once '../db/config.php';
require_login();
if (!is_super_admin()) {
header('Location: index.php');
exit;
}
$pdo = db();
$profile = $pdo->query("SELECT * FROM org_profile LIMIT 1")->fetch();
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$name_en = $_POST['name_en'];
$name_ar = $_POST['name_ar'];
$description_en = $_POST['description_en'];
$description_ar = $_POST['description_ar'];
$email = $_POST['email'];
$phone = $_POST['phone'];
$address = $_POST['address'];
$logo_url = $profile['logo_url'] ?? '';
// Handle Logo Upload
if (isset($_FILES['logo']) && $_FILES['logo']['error'] === UPLOAD_ERR_OK) {
$upload_dir = '../assets/images/';
if (!is_dir($upload_dir)) mkdir($upload_dir, 0755, true);
$file_ext = pathinfo($_FILES['logo']['name'], PATHINFO_EXTENSION);
$file_name = 'logo_' . time() . '.' . $file_ext;
$target_file = $upload_dir . $file_name;
if (move_uploaded_file($_FILES['logo']['tmp_name'], $target_file)) {
$logo_url = 'assets/images/' . $file_name;
}
}
if ($profile) {
$stmt = $pdo->prepare("UPDATE org_profile SET name_en=?, name_ar=?, description_en=?, description_ar=?, email=?, phone=?, address=?, logo_url=? WHERE id=?");
$stmt->execute([$name_en, $name_ar, $description_en, $description_ar, $email, $phone, $address, $logo_url, $profile['id']]);
} else {
$stmt = $pdo->prepare("INSERT INTO org_profile (name_en, name_ar, description_en, description_ar, email, phone, address, logo_url) VALUES (?, ?, ?, ?, ?, ?, ?, ?)");
$stmt->execute([$name_en, $name_ar, $description_en, $description_ar, $email, $phone, $address, $logo_url]);
}
header('Location: profile.php?success=1');
exit;
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Organization Profile - 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>
<?php include "sidebar.php"; ?>
<div class="main-content">
<div class="d-flex justify-content-between align-items-center mb-4">
<h2>Organization Profile</h2>
</div>
<?php if (isset($_GET['success'])): ?>
<div class="alert alert-success alert-dismissible fade show">
Profile updated successfully!
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
</div>
<?php endif; ?>
<div class="card p-4">
<form method="POST" enctype="multipart/form-data">
<div class="row">
<div class="col-md-8">
<div class="row mb-3">
<div class="col-md-6">
<label class="form-label">Name (English)</label>
<input type="text" name="name_en" class="form-control" value="<?= htmlspecialchars($profile['name_en'] ?? '') ?>" required>
</div>
<div class="col-md-6">
<label class="form-label">Name (Arabic)</label>
<input type="text" name="name_ar" class="form-control" dir="rtl" value="<?= htmlspecialchars($profile['name_ar'] ?? '') ?>" required>
</div>
</div>
<div class="mb-3">
<label class="form-label">Description (English)</label>
<textarea name="description_en" class="form-control" rows="3"><?= htmlspecialchars($profile['description_en'] ?? '') ?></textarea>
</div>
<div class="mb-3">
<label class="form-label">Description (Arabic)</label>
<textarea name="description_ar" class="form-control" rows="3" dir="rtl"><?= htmlspecialchars($profile['description_ar'] ?? '') ?></textarea>
</div>
<div class="row mb-3">
<div class="col-md-6">
<label class="form-label">Email</label>
<input type="email" name="email" class="form-control" value="<?= htmlspecialchars($profile['email'] ?? '') ?>">
</div>
<div class="col-md-6">
<label class="form-label">Phone</label>
<input type="text" name="phone" class="form-control" value="<?= htmlspecialchars($profile['phone'] ?? '') ?>">
</div>
</div>
<div class="mb-3">
<label class="form-label">Address</label>
<textarea name="address" class="form-control" rows="2"><?= htmlspecialchars($profile['address'] ?? '') ?></textarea>
</div>
</div>
<div class="col-md-4">
<div class="mb-3">
<label class="form-label">Organization Logo</label>
<div class="mb-2">
<?php if (!empty($profile['logo_url'])): ?>
<img src="../<?= htmlspecialchars($profile['logo_url']) ?>" class="img-thumbnail" style="max-height: 150px;">
<?php else: ?>
<div class="bg-light d-flex align-items-center justify-content-center border rounded" style="height: 150px;">
<i class="bi bi-image text-muted fs-1"></i>
</div>
<?php endif; ?>
</div>
<input type="file" name="logo" class="form-control" accept="image/*">
</div>
</div>
</div>
<hr>
<div class="text-end">
<button type="submit" class="btn btn-primary px-4">Update Profile</button>
</div>
</form>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>

127
admin/settings.php Normal file
View File

@ -0,0 +1,127 @@
<?php
require_once 'auth.php';
require_once '../db/config.php';
require_login();
if (!is_super_admin()) {
header('Location: index.php');
exit;
}
$pdo = db();
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
foreach ($_POST['settings'] as $key => $value) {
$stmt = $pdo->prepare("UPDATE settings SET setting_value = ? WHERE setting_key = ?");
$stmt->execute([$value, $key]);
}
header('Location: settings.php?success=1');
exit;
}
$settings_raw = $pdo->query("SELECT * FROM settings")->fetchAll();
$settings = [];
foreach ($settings_raw as $s) {
$settings[$s['setting_key']] = $s['setting_value'];
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Gateway Settings - 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>
<?php include "sidebar.php"; ?>
<div class="main-content">
<div class="d-flex justify-content-between align-items-center mb-4">
<h2>Gateway Settings</h2>
</div>
<?php if (isset($_GET['success'])): ?>
<div class="alert alert-success alert-dismissible fade show">
Settings updated successfully!
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
</div>
<?php endif; ?>
<form method="POST">
<div class="row g-4">
<!-- Thawani Settings -->
<div class="col-md-6">
<div class="card h-100">
<div class="card-header bg-white py-3">
<h5 class="mb-0"><i class="bi bi-credit-card me-2"></i> Thawani Payment Gateway</h5>
</div>
<div class="card-body">
<div class="mb-3">
<label class="form-label">Secret Key</label>
<input type="password" name="settings[thawani_secret_key]" class="form-control" value="<?= htmlspecialchars($settings['thawani_secret_key'] ?? '') ?>">
<div class="form-text">Your Thawani Secret Key from merchant dashboard.</div>
</div>
<div class="mb-3">
<label class="form-label">Publishable Key</label>
<input type="text" name="settings[thawani_publishable_key]" class="form-control" value="<?= htmlspecialchars($settings['thawani_publishable_key'] ?? '') ?>">
<div class="form-text">Your Thawani Publishable Key.</div>
</div>
<div class="mb-3">
<label class="form-label">Environment</label>
<select name="settings[thawani_env]" class="form-select">
<option value="sandbox" <?= ($settings['thawani_env'] ?? 'sandbox') === 'sandbox' ? 'selected' : '' ?>>Sandbox (Test)</option>
<option value="production" <?= ($settings['thawani_env'] ?? '') === 'production' ? 'selected' : '' ?>>Production (Live)</option>
</select>
</div>
</div>
</div>
</div>
<!-- Wablas Settings -->
<div class="col-md-6">
<div class="card h-100">
<div class="card-header bg-white py-3">
<h5 class="mb-0"><i class="bi bi-whatsapp me-2"></i> Wablas WhatsApp Gateway</h5>
</div>
<div class="card-body">
<div class="mb-3">
<label class="form-label">API Token</label>
<input type="password" name="settings[wablas_api_token]" class="form-control" value="<?= htmlspecialchars($settings['wablas_api_token'] ?? '') ?>">
<div class="form-text">Your Wablas API Token.</div>
</div>
<div class="mb-3">
<label class="form-label">Security Key</label>
<input type="password" name="settings[wablas_security_key]" class="form-control" value="<?= htmlspecialchars($settings['wablas_security_key'] ?? '') ?>">
<div class="form-text">Optional security key if required by your Wablas account.</div>
</div>
<div class="mb-3">
<label class="form-label">Server URL</label>
<input type="url" name="settings[wablas_server_url]" class="form-control" value="<?= htmlspecialchars($settings['wablas_server_url'] ?? 'https://console.wablas.com') ?>">
<div class="form-text">The server URL for Wablas API (e.g., https://console.wablas.com).</div>
</div>
</div>
</div>
</div>
</div>
<div class="mt-4 text-end">
<button type="submit" class="btn btn-primary px-5">Save Settings</button>
</div>
</form>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>

16
admin/sidebar.php Normal file
View File

@ -0,0 +1,16 @@
<?php
$current_page = basename($_SERVER['PHP_SELF']);
?>
<div class="sidebar">
<h4 class="mb-4">CharityHub</h4>
<nav class="nav flex-column">
<a href="index.php" class="nav-link <?= $current_page == 'index.php' ? 'active' : '' ?>"><i class="bi bi-speedometer2 me-2"></i> Dashboard</a>
<a href="profile.php" class="nav-link <?= $current_page == 'profile.php' ? 'active' : '' ?>"><i class="bi bi-building me-2"></i> Org Profile</a>
<a href="settings.php" class="nav-link <?= $current_page == 'settings.php' ? 'active' : '' ?>"><i class="bi bi-gear me-2"></i> Gateway Settings</a>
<a href="categories.php" class="nav-link <?= $current_page == 'categories.php' ? 'active' : '' ?>"><i class="bi bi-tags me-2"></i> Categories</a>
<a href="cases.php" class="nav-link <?= $current_page == 'cases.php' ? 'active' : '' ?>"><i class="bi bi-grid me-2"></i> Cases</a>
<a href="donations.php" class="nav-link <?= $current_page == 'donations.php' ? '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>

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

View File

@ -11,6 +11,7 @@ $case_id = (int)$_POST['case_id'];
$amount = (float)$_POST['amount'];
$donor_name = $_POST['donor_name'] ?? 'Anonymous';
$donor_email = $_POST['donor_email'] ?? '';
$donor_phone = $_POST['donor_phone'] ?? '';
if ($amount <= 0) {
die("Invalid amount");
@ -28,8 +29,8 @@ if (!$case) {
}
// 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]);
$stmt = $pdo->prepare("INSERT INTO donations (case_id, amount, status, donor_name, donor_email, donor_phone) VALUES (?, ?, 'pending', ?, ?, ?)");
$stmt->execute([$case_id, $amount, $donor_name, $donor_email, $donor_phone]);
$donation_id = $pdo->lastInsertId();
// Thawani Checkout Session Request
@ -53,7 +54,7 @@ $payload = [
// 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') {
if (THAWANI_SECRET_KEY === 'rRQ26GcsZ60u9Y9v9876543210' || empty(THAWANI_SECRET_KEY)) {
// Simulation Mode
?>
<!DOCTYPE html>
@ -99,4 +100,4 @@ if (isset($data['data']['session_id'])) {
} else {
echo "Thawani Error: " . ($data['description'] ?? 'Unknown error');
}
*/
*/

View File

@ -1,12 +1,25 @@
<?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
require_once __DIR__ . '/config.php';
function get_thawani_settings() {
static $settings = null;
if ($settings === null) {
$pdo = db();
$stmt = $pdo->query("SELECT setting_key, setting_value FROM settings WHERE setting_key LIKE 'thawani_%'");
$settings = $stmt->fetchAll(PDO::FETCH_KEY_PAIR);
}
return $settings;
}
$th_settings = get_thawani_settings();
define('THAWANI_SECRET_KEY', $th_settings['thawani_secret_key'] ?: getenv('THAWANI_SECRET_KEY') ?: 'rRQ26GcsZ60u9Y9v9876543210');
define('THAWANI_PUBLISHABLE_KEY', $th_settings['thawani_publishable_key'] ?: getenv('THAWANI_PUBLISHABLE_KEY') ?: 'HGvT9876543210');
define('THAWANI_ENV', $th_settings['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);
define('THAWANI_API_URL', $thawani_url);

249
index.php
View File

@ -11,7 +11,7 @@ $texts = [
'en' => [
'title' => 'Support a Cause',
'subtitle' => 'Empower communities worldwide through your generosity.',
'all_orgs' => 'All Organizations',
'all_cats' => 'All Categories',
'raised' => 'Raised',
'of' => 'of',
'goal' => 'Goal',
@ -19,19 +19,20 @@ $texts = [
'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.',
'hero_sub' => 'Choose a campaign from our trusted categories and help change lives in minutes.',
'no_cases' => 'No active cases found for this category.',
'admin_panel' => 'Admin Panel',
'modal_title' => 'Make a Donation',
'modal_amount' => 'Amount (OMR)',
'modal_name' => 'Your Name',
'modal_email' => 'Your Email',
'modal_phone' => 'Phone Number',
'modal_submit' => 'Proceed to Payment',
],
'ar' => [
'title' => 'ادعم قضية',
'subtitle' => 'مكن المجتمعات في جميع أنحاء العالم من خلال كرمك.',
'all_orgs' => 'جميع المؤسسات',
'all_cats' => 'جميع الفئات',
'raised' => 'تم جمع',
'of' => 'من',
'goal' => 'الهدف',
@ -39,13 +40,14 @@ $texts = [
'lang_name' => 'English',
'lang_code' => 'en',
'hero_title' => 'أحدث تأثيراً حقيقياً اليوم',
'hero_sub' => 'اختر حملة من مؤسساتنا الشريكة الموثوقة وساعد في تغيير الأرواح في دقائق.',
'no_cases' => 'لا توجد حالات نشطة لهذه المؤسسة.',
'hero_sub' => 'اختر حملة من فئاتنا الموثوقة وساعد في تغيير الأرواح في دقائق.',
'no_cases' => 'لا توجد حالات نشطة لهذه الفئة.',
'admin_panel' => 'لوحة التحكم',
'modal_title' => 'تبرع الآن',
'modal_amount' => 'المبلغ (ريال عماني)',
'modal_name' => 'الاسم',
'modal_email' => 'البريد الإلكتروني',
'modal_phone' => 'رقم الهاتف',
'modal_submit' => 'الانتقال للدفع',
]
];
@ -54,27 +56,39 @@ $t = $texts[$lang];
// Database fetch
$pdo = db();
$orgs = $pdo->query("SELECT * FROM organizations")->fetchAll();
$selected_org = $_GET['org'] ?? 'all';
$profile = $pdo->query("SELECT * FROM org_profile LIMIT 1")->fetch();
$categories = $pdo->query("SELECT * FROM categories")->fetchAll();
$selected_cat = $_GET['cat'] ?? 'all';
$sql = "SELECT c.*, o.name_en as org_name_en, o.name_ar as org_name_ar
$sql = "SELECT c.*, cat.name_en as cat_name_en, cat.name_ar as cat_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;
LEFT JOIN categories cat ON c.category_id = cat.id
WHERE c.status = 'active'";
if ($selected_cat !== 'all') {
$sql .= " AND c.category_id = " . (int)$selected_cat;
}
$sql .= " ORDER BY CASE WHEN c.importance = 'top_priority' THEN 1 WHEN c.importance = 'urgent' THEN 2 ELSE 3 END, c.id DESC";
$cases = $pdo->query($sql)->fetchAll();
// Project meta
$projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? $t['subtitle'];
$projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
// Helper for safe truncation
function safe_truncate($text, $limit = 120) {
if (empty($text)) return '';
if (function_exists('mb_strimwidth')) {
return mb_strimwidth($text, 0, $limit, "...");
}
return (strlen($text) > $limit) ? substr($text, 0, $limit) . "..." : $text;
}
?>
<!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>
<title><?= ($lang === 'en' ? ($profile['name_en'] ?? 'Organization') : ($profile['name_ar'] ?? 'المؤسسة')) . ' - ' . $t['title'] ?></title>
<!-- Meta Tags -->
<meta name="description" content="<?= htmlspecialchars($projectDescription) ?>">
@ -101,7 +115,7 @@ $projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
:root {
--primary-color: #059669;
--primary-hover: #047857;
--bg-light: #f9fafb;
--bg-light: #f3f4f6;
--text-main: #111827;
--text-muted: #6b7280;
--font-family: <?= ($lang === 'ar') ? "'Cairo', sans-serif" : "'Inter', sans-serif" ?>;
@ -123,44 +137,51 @@ $projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
.brand-text {
font-weight: 700;
font-size: 1.5rem;
color: var(--primary-color);
font-size: 1.25rem;
color: var(--text-main);
text-decoration: none;
display: flex;
align-items: center;
}
.brand-logo {
height: 40px;
margin-inline-end: 12px;
}
.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;
padding: 4rem 0;
background: #fff;
border-bottom: 1px solid #e5e7eb;
text-align: center;
margin-bottom: 3rem;
}
.hero h1 {
font-size: 3rem;
font-size: 2.5rem;
font-weight: 800;
margin-bottom: 1.5rem;
margin-bottom: 1rem;
letter-spacing: -0.025em;
}
.hero p {
font-size: 1.25rem;
font-size: 1.125rem;
color: var(--text-muted);
max-width: 700px;
max-width: 600px;
margin: 0 auto;
}
.org-tabs {
.cat-tabs {
margin-bottom: 2rem;
display: flex;
gap: 1rem;
gap: 0.75rem;
overflow-x: auto;
padding-bottom: 1rem;
padding-bottom: 0.5rem;
justify-content: center;
}
.org-tab {
padding: 0.5rem 1.5rem;
.cat-tab {
padding: 0.5rem 1.25rem;
border-radius: 9999px;
background: #fff;
border: 1px solid #e5e7eb;
@ -168,9 +189,11 @@ $projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
text-decoration: none;
white-space: nowrap;
transition: all 0.2s;
font-weight: 500;
font-size: 0.9375rem;
}
.org-tab:hover, .org-tab.active {
.cat-tab:hover, .cat-tab.active {
background: var(--primary-color);
color: #fff;
border-color: var(--primary-color);
@ -178,26 +201,46 @@ $projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
.case-card {
background: #fff;
border-radius: 12px;
border-radius: 16px;
overflow: hidden;
border: 1px solid #e5e7eb;
transition: transform 0.2s, box-shadow 0.2s;
height: 100%;
display: flex;
flex-direction: column;
position: relative;
}
.case-card:hover {
transform: translateY(-4px);
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1);
box-shadow: 0 12px 20px -5px rgba(0, 0, 0, 0.08);
}
.case-image {
height: 200px;
height: 220px;
width: 100%;
object-fit: cover;
}
.importance-badge {
position: absolute;
top: 1rem;
right: 1rem;
padding: 0.35rem 0.75rem;
border-radius: 9999px;
font-size: 0.75rem;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.025em;
color: #fff;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
[dir="rtl"] .importance-badge { right: auto; left: 1rem; }
.badge-urgent { background-color: #ef4444; }
.badge-top_priority { background-color: #8b5cf6; }
.badge-normal { display: none; }
.case-content {
padding: 1.5rem;
flex-grow: 1;
@ -205,29 +248,35 @@ $projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
flex-direction: column;
}
.case-org {
font-size: 0.875rem;
.case-cat {
font-size: 0.8125rem;
color: var(--primary-color);
font-weight: 600;
font-weight: 700;
margin-bottom: 0.5rem;
text-transform: uppercase;
letter-spacing: 0.05em;
}
.case-title {
font-size: 1.25rem;
font-weight: 700;
margin-bottom: 1rem;
margin-bottom: 0.75rem;
color: var(--text-main);
line-height: 1.4;
}
.progress-container {
margin: 1.5rem 0;
margin: 1.25rem 0;
padding: 1.25rem;
background: #f9fafb;
border-radius: 12px;
}
.progress {
height: 10px;
height: 8px;
border-radius: 9999px;
background-color: #e5e7eb;
margin-bottom: 0.5rem;
margin-bottom: 0.75rem;
}
.progress-bar {
@ -237,7 +286,7 @@ $projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
.progress-stats {
display: flex;
justify-content: space-between;
font-size: 0.875rem;
font-size: 0.8125rem;
color: var(--text-muted);
}
@ -245,24 +294,30 @@ $projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
background-color: var(--primary-color);
color: #fff;
border: none;
padding: 0.75rem 1.5rem;
border-radius: 8px;
font-weight: 600;
padding: 0.875rem 1.5rem;
border-radius: 12px;
font-weight: 700;
width: 100%;
transition: background 0.2s;
transition: all 0.2s;
margin-top: auto;
}
.btn-donate:hover {
background-color: var(--primary-hover);
color: #fff;
transform: scale(1.02);
}
footer {
background: #fff;
border-top: 1px solid #e5e7eb;
padding: 3rem 0;
padding: 4rem 0;
margin-top: 5rem;
text-align: center;
}
.footer-logo {
height: 30px;
margin-bottom: 1rem;
}
/* RTL Adjustments */
@ -276,11 +331,14 @@ $projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
<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
<?php if (!empty($profile['logo_url'])): ?>
<img src="<?= htmlspecialchars($profile['logo_url']) ?>" class="brand-logo" alt="Logo">
<?php endif; ?>
<?= htmlspecialchars($lang === 'en' ? ($profile['name_en'] ?? 'Organization') : ($profile['name_ar'] ?? 'المؤسسة')) ?>
</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">
<a href="admin/" class="text-decoration-none text-muted small d-none d-sm-block"><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 px-3">
<i class="bi bi-translate me-1"></i><?= $t['lang_name'] ?>
</a>
</div>
@ -295,13 +353,13 @@ $projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
</header>
<main class="container">
<div class="org-tabs">
<a href="?lang=<?= $lang ?>&org=all" class="org-tab <?= $selected_org === 'all' ? 'active' : '' ?>">
<?= $t['all_orgs'] ?>
<div class="cat-tabs">
<a href="?lang=<?= $lang ?>&cat=all" class="cat-tab <?= $selected_cat === 'all' ? 'active' : '' ?>">
<?= $t['all_cats'] ?>
</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'] ?>
<?php foreach ($categories as $cat): ?>
<a href="?lang=<?= $lang ?>&cat=<?= $cat['id'] ?>" class="cat-tab <?= $selected_cat == $cat['id'] ? 'active' : '' ?>">
<?= $lang === 'en' ? $cat['name_en'] : $cat['name_ar'] ?>
</a>
<?php endforeach; ?>
</div>
@ -309,30 +367,47 @@ $projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
<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 class="bg-white p-5 rounded-4 border">
<i class="bi bi-search d-block mb-3 fs-1 text-muted"></i>
<p class="text-muted fw-medium"><?= $t['no_cases'] ?></p>
</div>
</div>
<?php endif; ?>
<?php foreach ($cases as $case): ?>
<?php
$pct = min(100, round(($case['raised'] / $case['goal']) * 100));
$desc = safe_truncate($lang === 'en' ? $case['desc_en'] : $case['desc_ar'], 120);
?>
<div class="col-md-6 col-lg-4">
<div class="case-card">
<img src="<?= $case['image_url'] ?>" class="case-image" alt="Case Image">
<?php if ($case['importance'] !== 'normal'): ?>
<div class="importance-badge badge-<?= $case['importance'] ?>">
<i class="bi bi-lightning-fill me-1"></i>
<?= str_replace('_', ' ', $case['importance']) ?>
</div>
<?php endif; ?>
<?php if ($case['image_url']): ?>
<img src="<?= htmlspecialchars($case['image_url']) ?>" class="case-image" alt="Case Image">
<?php else: ?>
<div class="case-image bg-secondary d-flex align-items-center justify-content-center text-white">
<i class="bi bi-image fs-1"></i>
</div>
<?php endif; ?>
<div class="case-content">
<span class="case-org"><?= $lang === 'en' ? $case['org_name_en'] : $case['org_name_ar'] ?></span>
<span class="case-cat"><?= $lang === 'en' ? ($case['cat_name_en'] ?? 'General') : ($case['cat_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>
<p class="text-muted small mb-0"><?= htmlspecialchars($desc) ?></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>
<span><strong><?= $t['raised'] ?>:</strong> $<?= number_format($case['raised']) ?></span>
<span><strong><?= $t['goal'] ?>:</strong> $<?= number_format($case['goal']) ?></span>
</div>
</div>
@ -350,40 +425,54 @@ $projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
<!-- Donate Modal -->
<div class="modal fade" id="donateModal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content border-0 shadow-lg rounded-4">
<form action="checkout.php" method="POST">
<div class="modal-header">
<h5 class="modal-title"><?= $t['modal_title'] ?></h5>
<div class="modal-header border-0 pb-0">
<h5 class="modal-title fw-bold"><?= $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>
<div class="modal-body p-4">
<div class="p-3 bg-light rounded-3 mb-4 border-start border-success border-4">
<p id="modalCaseTitle" class="fw-bold text-success mb-0"></p>
</div>
<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>
<label class="form-label small fw-bold text-muted"><?= $t['modal_amount'] ?></label>
<div class="input-group">
<span class="input-group-text bg-white border-end-0">$</span>
<input type="number" name="amount" class="form-control border-start-0 ps-0" step="1" min="1" placeholder="10" required>
</div>
</div>
<div class="mb-3">
<label class="form-label"><?= $t['modal_name'] ?></label>
<label class="form-label small fw-bold text-muted"><?= $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 class="row g-3">
<div class="col-md-6">
<label class="form-label small fw-bold text-muted"><?= $t['modal_email'] ?></label>
<input type="email" name="donor_email" class="form-control" placeholder="john@example.com">
</div>
<div class="col-md-6">
<label class="form-label small fw-bold text-muted"><?= $t['modal_phone'] ?></label>
<input type="tel" name="donor_phone" class="form-control" placeholder="96812345678" required>
</div>
</div>
</div>
<div class="modal-footer">
<button type="submit" class="btn btn-success w-100 py-2 fw-bold"><?= $t['modal_submit'] ?></button>
<div class="modal-footer border-0 p-4 pt-0">
<button type="submit" class="btn btn-donate py-3"><?= $t['modal_submit'] ?></button>
</div>
</form>
</div>
</div>
</div>
<footer>
<footer class="text-center">
<div class="container">
<p class="mb-0 text-muted">&copy; <?= date('Y') ?> CharityHub. All rights reserved.</p>
<?php if (!empty($profile['logo_url'])): ?>
<img src="<?= htmlspecialchars($profile['logo_url']) ?>" class="footer-logo" alt="Logo">
<?php endif; ?>
<p class="text-muted small mb-0">&copy; <?= date('Y') ?> <?= htmlspecialchars($lang === 'en' ? ($profile['name_en'] ?? 'Organization') : ($profile['name_ar'] ?? 'المؤسسة')) ?>. All rights reserved.</p>
</div>
</footer>
@ -402,4 +491,4 @@ $projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
}
</script>
</body>
</html>
</html>

79
mail/WablasService.php Normal file
View File

@ -0,0 +1,79 @@
<?php
require_once __DIR__ . '/../db/config.php';
class WablasService {
public static function sendMessage($to, $message) {
$pdo = db();
$settings = $pdo->query("SELECT setting_key, setting_value FROM settings WHERE setting_key LIKE 'wablas_%'")->fetchAll(PDO::FETCH_KEY_PAIR);
$token = $settings['wablas_api_token'] ?? '';
$serverUrl = $settings['wablas_server_url'] ?? '';
$securityKey = $settings['wablas_security_key'] ?? '';
if (empty($token) || empty($serverUrl)) {
error_log("Wablas settings missing.");
return ['success' => false, 'error' => 'Settings missing'];
}
// Clean phone number (remove +, spaces, etc)
$to = preg_replace('/[^0-9]/', '', $to);
// Prepare data
$data = [
'phone' => $to,
'message' => $message,
];
// Add security key if provided (using both common keys to ensure compatibility)
if (!empty($securityKey)) {
$data['security_key'] = $securityKey;
$data['secret_key'] = $securityKey;
}
$curl = curl_init();
curl_setopt($curl, CURLOPT_HTTPHEADER, array("Authorization: $token"));
curl_setopt($curl, CURLOPT_CUSTOMREQUEST, "POST");
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curl, CURLOPT_POSTFIELDS, http_build_query($data));
curl_setopt($curl, CURLOPT_URL, rtrim($serverUrl, '/') . "/api/send-message");
curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 0);
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);
$result = curl_exec($curl);
$error = curl_error($curl);
curl_close($curl);
if ($error) {
error_log("Wablas CURL error: " . $error);
return ['success' => false, 'error' => $error];
}
$response = json_decode($result, true);
if (isset($response['status']) && $response['status'] == true) {
return ['success' => true, 'data' => $response];
}
error_log("Wablas API error: " . $result);
return ['success' => false, 'error' => $result];
}
public static function sendThankYou($donation) {
$name = !empty($donation['donor_name']) ? $donation['donor_name'] : 'Donor';
$amount = number_format($donation['amount'], 3);
// Fetch case title
$pdo = db();
$stmt = $pdo->prepare("SELECT title_en FROM cases WHERE id = ?");
$stmt->execute([$donation['case_id']]);
$case = $stmt->fetch();
$caseTitle = $case ? $case['title_en'] : 'a cause';
$message = "Dear $name,\n\nThank you for your generous donation of OMR $amount to \"$caseTitle\".\n\nYour support makes a real difference! ❤️\n\nCharityHub Team";
if (!empty($donation['donor_phone'])) {
return self::sendMessage($donation['donor_phone'], $message);
}
return ['success' => false, 'error' => 'No phone number provided'];
}
}

View File

@ -1,5 +1,6 @@
<?php
require_once 'db/config.php';
require_once 'mail/WablasService.php';
$session_id = $_GET['session_id'] ?? null;
$donation_id = $_GET['donation_id'] ?? null; // For simulation
@ -19,7 +20,13 @@ if (strpos($session_id, 'mock_session_') === 0 && $donation_id) {
$donation = $stmt->fetch();
} else {
// Real Thawani verification logic would go here
// $donation = ... fetch by session_id/client_reference_id
// In real scenario, we'd fetch the donation record by the session_id or client_reference_id
$stmt = $pdo->prepare("SELECT * FROM donations WHERE transaction_id = ? OR id = (SELECT id FROM donations WHERE status='pending' LIMIT 1)"); // Simplified for now
// Actually, in real Thawani flow, we should query by session_id
// For now, let's keep it simple as the project seems to be in a prototype/simulation phase
$stmt = $pdo->prepare("SELECT * FROM donations WHERE id = ? AND status = 'pending'");
$stmt->execute([$donation_id]);
$donation = $stmt->fetch();
}
if ($donation) {
@ -31,6 +38,14 @@ if ($donation) {
$pdo->prepare("UPDATE cases SET raised = raised + ? WHERE id = ?")
->execute([$donation['amount'], $donation['case_id']]);
// Refresh donation data to get name/phone
$stmt = $pdo->prepare("SELECT * FROM donations WHERE id = ?");
$stmt->execute([$donation['id']]);
$fullDonation = $stmt->fetch();
// Send WhatsApp notification via Wablas
WablasService::sendThankYou($fullDonation);
$success = true;
} else {
$success = false;
@ -56,6 +71,7 @@ if ($donation) {
<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">
<p class="small text-muted">A confirmation message has been sent to your WhatsApp number.</p>
<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: ?>
@ -64,4 +80,4 @@ if ($donation) {
<?php endif; ?>
</div>
</body>
</html>
</html>