add financial summery

This commit is contained in:
Flatlogic Bot 2026-02-13 09:21:53 +00:00
parent a4dccb579a
commit 699f776eee
12 changed files with 766 additions and 10 deletions

80
admin/audit_logs.php Normal file
View File

@ -0,0 +1,80 @@
<?php
require_once 'auth.php';
require_once '../db/config.php';
require_login();
$user = get_user();
$pdo = db();
// Fetch audit logs with user info
$logs = $pdo->query("
SELECT l.*, u.email as user_email
FROM audit_logs l
LEFT JOIN users u ON l.user_id = u.id
ORDER BY l.created_at DESC
LIMIT 100
")->fetchAll();
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Audit Logs - 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 class="mb-0">Activity Audit Logs</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">User</th>
<th>Action</th>
<th>Details</th>
<th>Date</th>
</tr>
</thead>
<tbody>
<?php foreach ($logs as $log): ?>
<tr>
<td class="ps-4">
<strong><?= htmlspecialchars($log['user_email'] ?? 'System/Unknown') ?></strong>
</td>
<td>
<span class="badge bg-secondary rounded-pill"><?= htmlspecialchars($log['action']) ?></span>
</td>
<td><?= htmlspecialchars($log['details']) ?></td>
<td><?= date('Y-m-d H:i:s', strtotime($log['created_at'])) ?></td>
</tr>
<?php endforeach; ?>
<?php if (empty($logs)): ?>
<tr>
<td colspan="4" class="text-center py-4 text-muted">No logs found.</td>
</tr>
<?php endif; ?>
</tbody>
</table>
</div>
</div>
</div>
</body>
</html>

View File

@ -43,4 +43,16 @@ function get_user() {
function is_super_admin() {
$user = get_user();
return $user && $user['role'] === 'super_admin';
}
}
/**
* Log an administrative action to the audit_logs table
*/
function log_action($action, $details = null) {
require_once __DIR__ . '/../db/config.php';
$pdo = db();
$userId = $_SESSION['user_id'] ?? null;
$stmt = $pdo->prepare("INSERT INTO audit_logs (user_id, action, details) VALUES (?, ?, ?)");
$stmt->execute([$userId, $action, $details]);
}

View File

@ -6,10 +6,41 @@ require_login();
$pdo = db();
$msg = '';
/**
* Basic Image Optimization
*/
function optimize_image($source, $destination, $quality = 80) {
$info = getimagesize($source);
if ($info['mime'] == 'image/jpeg') {
$image = imagecreatefromjpeg($source);
imagejpeg($image, $destination, $quality);
} elseif ($info['mime'] == 'image/png') {
$image = imagecreatefrompng($source);
imagepalettetotruecolor($image);
imagealphablending($image, false);
imagesavealpha($image, true);
imagepng($image, $destination, round(9 * ($quality/100)));
} elseif ($info['mime'] == 'image/webp') {
$image = imagecreatefromwebp($source);
imagewebp($image, $destination, $quality);
}
return $destination;
}
// Handle Delete
if (isset($_GET['delete'])) {
$id = (int)$_GET['delete'];
$pdo->prepare("DELETE FROM cases WHERE id = ?")->execute([$id]);
// Fetch title for logging before deleting
$stmt = $pdo->prepare("SELECT title_en FROM cases WHERE id = ?");
$stmt->execute([$id]);
$case_to_delete = $stmt->fetch();
if ($case_to_delete) {
$pdo->prepare("DELETE FROM cases WHERE id = ?")->execute([$id]);
log_action('delete_case', "Deleted case: " . $case_to_delete['title_en'] . " (ID: $id)");
}
header('Location: cases.php?success=deleted');
exit;
}
@ -40,7 +71,14 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$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)) {
$temp_path = $_FILES['image']['tmp_name'];
$target_path = $upload_dir . $filename;
if (move_uploaded_file($temp_path, $target_path)) {
// Optimize the uploaded image
if ($ext !== 'gif') {
optimize_image($target_path, $target_path, 75);
}
$image_url = 'assets/images/cases/' . $filename;
}
}
@ -49,9 +87,12 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
if ($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]);
log_action('edit_case', "Updated case: $title_en (ID: $id)");
} else {
$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]);
$new_id = $pdo->lastInsertId();
log_action('create_case', "Created new case: $title_en (ID: $new_id)");
}
header('Location: cases.php?success=saved');
exit;

View File

@ -40,7 +40,10 @@ $donations = $pdo->query("SELECT d.*, c.title_en as case_title, cat.name_en as c
<div class="main-content">
<div class="d-flex justify-content-between align-items-center mb-4">
<h2>Donations History</h2>
<h2 class="mb-0">Donations History</h2>
<a href="export_donations.php" class="btn btn-outline-success">
<i class="bi bi-file-earmark-excel me-2"></i>Export to CSV
</a>
</div>
<div class="card p-0">
@ -61,6 +64,9 @@ $donations = $pdo->query("SELECT d.*, c.title_en as case_title, cat.name_en as c
<tr>
<td class="ps-4">
<strong><?= htmlspecialchars($don['donor_name'] ?: 'Anonymous') ?></strong>
<?php if ($don['is_gift']): ?>
<span class="badge bg-info-subtle text-info rounded-pill ms-2" style="font-size: 0.7rem;">Gift</span>
<?php endif; ?>
</td>
<td>
<div class="small"><?= htmlspecialchars($don['donor_email']) ?></div>

88
admin/donors.php Normal file
View File

@ -0,0 +1,88 @@
<?php
require_once 'auth.php';
require_once '../db/config.php';
require_login();
$user = get_user();
$pdo = db();
// Fetch unique donors based on email, with total amount and count
$donors = $pdo->query("
SELECT
donor_email,
donor_name,
donor_phone,
SUM(amount) as total_contributed,
COUNT(*) as donation_count,
MAX(created_at) as last_donation
FROM donations
WHERE status = 'completed'
GROUP BY donor_email
ORDER BY total_contributed DESC
")->fetchAll();
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Donors CRM - 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 class="mb-0">Donor CRM</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 Name</th>
<th>Email</th>
<th>Phone</th>
<th>Donations</th>
<th>Total Amount</th>
<th>Last Donation</th>
</tr>
</thead>
<tbody>
<?php foreach ($donors as $donor): ?>
<tr>
<td class="ps-4">
<strong><?= htmlspecialchars($donor['donor_name'] ?: 'Anonymous') ?></strong>
</td>
<td><?= htmlspecialchars($donor['donor_email']) ?></td>
<td><?= htmlspecialchars($donor['donor_phone'] ?? 'N/A') ?></td>
<td><span class="badge bg-primary rounded-pill"><?= $donor['donation_count'] ?></span></td>
<td><strong>OMR <?= number_format($donor['total_contributed'], 3) ?></strong></td>
<td class="small text-muted"><?= date('M j, Y', strtotime($donor['last_donation'])) ?></td>
</tr>
<?php endforeach; ?>
<?php if (empty($donors)): ?>
<tr>
<td colspan="6" class="text-center py-4 text-muted">No donors found with completed payments.</td>
</tr>
<?php endif; ?>
</tbody>
</table>
</div>
</div>
</div>
</body>
</html>

View File

@ -0,0 +1,47 @@
<?php
require_once 'auth.php';
require_once '../db/config.php';
require_login();
$pdo = db();
// Fetch all donations with details
$stmt = $pdo->query("
SELECT d.id, d.donor_name, d.donor_email, d.donor_phone, c.title_en as case_title, d.amount, d.status, d.transaction_id, d.created_at, d.is_gift, d.gift_recipient_name, d.gift_recipient_phone
FROM donations d
JOIN cases c ON d.case_id = c.id
ORDER BY d.created_at DESC
");
$filename = "donations_" . date('Y-m-d') . ".csv";
header('Content-Type: text/csv; charset=utf-8');
header('Content-Disposition: attachment; filename=' . $filename);
$output = fopen('php://output', 'w');
// Set CSV headers
fputcsv($output, [
'ID',
'Donor Name',
'Donor Email',
'Donor Phone',
'Case',
'Amount (OMR)',
'Status',
'Transaction ID',
'Date',
'Is Gift',
'Recipient Name',
'Recipient Phone'
]);
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
// Clean up status for CSV
$row['status'] = ucfirst($row['status']);
$row['is_gift'] = $row['is_gift'] ? 'Yes' : 'No';
fputcsv($output, $row);
}
fclose($output);
exit;

281
admin/financial_summary.php Normal file
View File

@ -0,0 +1,281 @@
<?php
require_once 'auth.php';
require_once '../db/config.php';
require_login();
$user = get_user();
$pdo = db();
// Basic Stats
$stats = $pdo->query("
SELECT
COUNT(*) as total_count,
SUM(CASE WHEN status = 'completed' THEN amount ELSE 0 END) as total_revenue,
AVG(CASE WHEN status = 'completed' THEN amount ELSE NULL END) as avg_donation,
SUM(CASE WHEN status = 'pending' THEN 1 ELSE 0 END) as pending_count,
SUM(CASE WHEN status = 'completed' THEN 1 ELSE 0 END) as completed_count
FROM donations
")->fetch();
// Revenue by Category
$category_revenue = $pdo->query("
SELECT c.name_en, SUM(d.amount) as total
FROM categories c
JOIN cases cs ON cs.category_id = c.id
JOIN donations d ON d.case_id = cs.id
WHERE d.status = 'completed'
GROUP BY c.id
ORDER BY total DESC
")->fetchAll();
$cat_labels = [];
$cat_totals = [];
foreach ($category_revenue as $row) {
$cat_labels[] = $row['name_en'];
$cat_totals[] = (float)$row['total'];
}
// Monthly Revenue Trend (Last 12 Months)
$monthly_trend = $pdo->query("
SELECT
DATE_FORMAT(created_at, '%Y-%m') as month,
SUM(amount) as total
FROM donations
WHERE status = 'completed' AND created_at >= DATE_SUB(NOW(), INTERVAL 12 MONTH)
GROUP BY month
ORDER BY month ASC
")->fetchAll();
$trend_labels = [];
$trend_totals = [];
foreach ($monthly_trend as $row) {
$trend_labels[] = date('M Y', strtotime($row['month'] . '-01'));
$trend_totals[] = (float)$row['total'];
}
// Top Cases by Revenue
$top_cases = $pdo->query("
SELECT cs.title_en, SUM(d.amount) as total, cs.goal
FROM cases cs
JOIN donations d ON d.case_id = cs.id
WHERE d.status = 'completed'
GROUP BY cs.id
ORDER BY total DESC
LIMIT 5
")->fetchAll();
// Gift vs Regular
$gift_stats = $pdo->query("
SELECT
is_gift,
COUNT(*) as count,
SUM(amount) as total
FROM donations
WHERE status = 'completed'
GROUP BY is_gift
")->fetchAll();
$gift_labels = ['Regular', 'Gift'];
$gift_totals = [0, 0];
foreach ($gift_stats as $row) {
if ($row['is_gift']) {
$gift_totals[1] = (float)$row['total'];
} else {
$gift_totals[0] = (float)$row['total'];
}
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Financial Summary - 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">
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<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; }
.card { border: none; border-radius: 12px; box-shadow: 0 1px 3px rgba(0,0,0,0.1); }
.stat-card { padding: 1.5rem; border-left: 4px solid var(--primary-color); }
.chart-container { position: relative; height: 250px; }
</style>
</head>
<body>
<?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">Financial Summary</h2>
<p class="text-muted mb-0">Detailed analysis of donations and revenue streams.</p>
</div>
<div class="text-muted"><?= date('l, F j, Y') ?></div>
</div>
<!-- Summary Cards -->
<div class="row g-4 mb-4">
<div class="col-md-3">
<div class="card stat-card">
<div class="text-muted small">Total Revenue</div>
<div class="h3 mb-0">OMR <?= number_format($stats['total_revenue'] ?? 0, 3) ?></div>
<div class="text-success small"><i class="bi bi-check-circle"></i> From <?= $stats['completed_count'] ?> donations</div>
</div>
</div>
<div class="col-md-3">
<div class="card stat-card" style="border-left-color: #3b82f6;">
<div class="text-muted small">Avg. Donation</div>
<div class="h3 mb-0">OMR <?= number_format($stats['avg_donation'] ?? 0, 3) ?></div>
<div class="text-primary small">Per completed donation</div>
</div>
</div>
<div class="col-md-3">
<div class="card stat-card" style="border-left-color: #f59e0b;">
<div class="text-muted small">Pending Revenue</div>
<div class="h3 mb-0"><?= $stats['pending_count'] ?></div>
<div class="text-warning small">Awaiting payment</div>
</div>
</div>
<div class="col-md-3">
<div class="card stat-card" style="border-left-color: #ec4899;">
<div class="text-muted small">Total Donations</div>
<div class="h3 mb-0"><?= $stats['total_count'] ?></div>
<div class="text-danger small">All statuses included</div>
</div>
</div>
</div>
<div class="row g-4 mb-4">
<!-- Monthly Trend -->
<div class="col-lg-8">
<div class="card p-4 h-100">
<h5 class="mb-4">Monthly Revenue Trend (Last 12 Months)</h5>
<div style="height: 300px;">
<canvas id="trendChart"></canvas>
</div>
</div>
</div>
<!-- Category Distribution -->
<div class="col-lg-4">
<div class="card p-4 h-100">
<h5 class="mb-4">Revenue by Category</h5>
<div class="chart-container">
<canvas id="categoryChart"></canvas>
</div>
</div>
</div>
</div>
<div class="row g-4 mb-4">
<!-- Top Cases -->
<div class="col-lg-6">
<div class="card p-4 h-100">
<h5 class="mb-4">Top Performing Cases</h5>
<div class="list-group list-group-flush">
<?php foreach ($top_cases as $case): ?>
<div class="list-group-item px-0 border-0 mb-3">
<div class="d-flex justify-content-between mb-1">
<span class="fw-bold"><?= htmlspecialchars($case['title_en']) ?></span>
<span>OMR <?= number_format($case['total'], 3) ?></span>
</div>
<?php
$percent = $case['goal'] > 0 ? ($case['total'] / $case['goal']) * 100 : 0;
?>
<div class="progress" style="height: 8px;">
<div class="progress-bar bg-success" role="progressbar" style="width: <?= min(100, $percent) ?>%"></div>
</div>
<div class="text-muted small mt-1"><?= number_format($percent, 1) ?>% of OMR <?= number_format($case['goal'], 0) ?> goal</div>
</div>
<?php endforeach; ?>
</div>
</div>
</div>
<!-- Gift vs Regular -->
<div class="col-lg-6">
<div class="card p-4 h-100">
<h5 class="mb-4">Regular vs Gift Donations (Revenue)</h5>
<div class="d-flex align-items-center justify-content-center" style="height: 250px;">
<canvas id="giftChart"></canvas>
</div>
<div class="mt-4 row text-center">
<div class="col-6">
<div class="text-muted small">Regular</div>
<div class="h5">OMR <?= number_format($gift_totals[0], 3) ?></div>
</div>
<div class="col-6">
<div class="text-muted small">Gift</div>
<div class="h5">OMR <?= number_format($gift_totals[1], 3) ?></div>
</div>
</div>
</div>
</div>
</div>
</div>
<script>
// Trend Chart
new Chart(document.getElementById('trendChart'), {
type: 'bar',
data: {
labels: <?= json_encode($trend_labels) ?>,
datasets: [{
label: 'Revenue (OMR)',
data: <?= json_encode($trend_totals) ?>,
backgroundColor: '#059669',
borderRadius: 6
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
scales: {
y: { beginAtZero: true, grid: { color: '#f3f4f6' } },
x: { grid: { display: false } }
}
}
});
// Category Chart
new Chart(document.getElementById('categoryChart'), {
type: 'doughnut',
data: {
labels: <?= json_encode($cat_labels) ?>,
datasets: [{
data: <?= json_encode($cat_totals) ?>,
backgroundColor: ['#059669', '#3b82f6', '#f59e0b', '#ec4899', '#8b5cf6', '#06b6d4']
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: { position: 'bottom', labels: { boxWidth: 12 } }
}
}
});
// Gift Chart
new Chart(document.getElementById('giftChart'), {
type: 'pie',
data: {
labels: <?= json_encode($gift_labels) ?>,
datasets: [{
data: <?= json_encode($gift_totals) ?>,
backgroundColor: ['#10b981', '#f472b6']
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: { display: false }
}
}
});
</script>
</body>
</html>

View File

@ -11,6 +11,22 @@ $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 chart data (last 30 days)
$chart_data = $pdo->query("
SELECT DATE(created_at) as date, SUM(amount) as total
FROM donations
WHERE status = 'completed' AND created_at >= DATE_SUB(NOW(), INTERVAL 30 DAY)
GROUP BY DATE(created_at)
ORDER BY date ASC
")->fetchAll(PDO::FETCH_ASSOC);
$labels = [];
$totals = [];
foreach ($chart_data as $row) {
$labels[] = date('M j', strtotime($row['date']));
$totals[] = (float)$row['total'];
}
// Fetch recent donations
$recent_donations = $pdo->query("
SELECT d.*, c.title_en as case_title
@ -28,6 +44,7 @@ $recent_donations = $pdo->query("
<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">
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<style>
:root { --sidebar-width: 260px; --primary-color: #059669; }
body { background-color: #f3f4f6; }
@ -42,6 +59,7 @@ $recent_donations = $pdo->query("
.badge-pending { background-color: #fef3c7; color: #92400e; }
.badge-completed { background-color: #d1fae5; color: #065f46; }
.badge-failed { background-color: #fee2e2; color: #991b1b; }
.chart-container { height: 300px; position: relative; }
</style>
</head>
<body>
@ -92,6 +110,27 @@ $recent_donations = $pdo->query("
</div>
</div>
<div class="row g-4 mb-4">
<div class="col-lg-8">
<div class="card p-4">
<h5 class="mb-4">Donation Trends (Last 30 Days)</h5>
<div class="chart-container">
<canvas id="donationsChart"></canvas>
</div>
</div>
</div>
<div class="col-lg-4">
<div class="card p-4 h-100">
<h5 class="mb-4">Quick Links</h5>
<div class="d-grid gap-2">
<a href="cases.php" class="btn btn-outline-primary text-start"><i class="bi bi-plus-circle me-2"></i> Create New Case</a>
<a href="donations.php" class="btn btn-outline-success text-start"><i class="bi bi-download me-2"></i> Export Donations</a>
<a href="settings.php" class="btn btn-outline-secondary text-start"><i class="bi bi-gear me-2"></i> System Settings</a>
</div>
</div>
</div>
</div>
<div class="card p-4">
<div class="d-flex justify-content-between align-items-center mb-3">
<h5 class="mb-0">Recent Donations</h5>
@ -136,5 +175,42 @@ $recent_donations = $pdo->query("
</div>
</div>
</div>
<script>
const ctx = document.getElementById('donationsChart').getContext('2d');
new Chart(ctx, {
type: 'line',
data: {
labels: <?= json_encode($labels) ?>,
datasets: [{
label: 'Donations (OMR)',
data: <?= json_encode($totals) ?>,
borderColor: '#059669',
backgroundColor: 'rgba(5, 150, 105, 0.1)',
fill: true,
tension: 0.4,
borderWidth: 3,
pointRadius: 4,
pointBackgroundColor: '#059669'
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: { display: false }
},
scales: {
y: {
beginAtZero: true,
grid: { color: '#f3f4f6' }
},
x: {
grid: { display: false }
}
}
}
});
</script>
</body>
</html>

View File

@ -33,6 +33,9 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
// Regenerate ID for security
session_regenerate_id(true);
// Log the successful login
log_action('login', "User $email logged in successfully.");
header('Location: index.php');
exit;
}

View File

@ -10,7 +10,10 @@ $current_page = basename($_SERVER['PHP_SELF']);
<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>
<a href="financial_summary.php" class="nav-link <?= $current_page == 'financial_summary.php' ? 'active' : '' ?>"><i class="bi bi-graph-up-arrow me-2"></i> Financial Summary</a>
<a href="donors.php" class="nav-link <?= $current_page == 'donors.php' ? 'active' : '' ?>"><i class="bi bi-people me-2"></i> Donors (CRM)</a>
<a href="audit_logs.php" class="nav-link <?= $current_page == 'audit_logs.php' ? 'active' : '' ?>"><i class="bi bi-journal-text me-2"></i> Audit Logs</a>
<hr>
<a href="logout.php" class="nav-link"><i class="bi bi-box-arrow-right me-2"></i> Logout</a>
<a href="logout.php" class="nav-link text-danger"><i class="bi bi-box-arrow-right me-2"></i> Logout</a>
</nav>
</div>
</div>

106
certificate.php Normal file
View File

@ -0,0 +1,106 @@
<?php
require_once 'db/config.php';
$donation_id = $_GET['id'] ?? null;
if (!$donation_id) exit('Invalid donation ID');
$pdo = db();
$stmt = $pdo->prepare("
SELECT d.*, c.title_en as case_title, c.title_ar as case_title_ar
FROM donations d
JOIN cases c ON d.case_id = c.id
WHERE d.id = ? AND d.status = 'completed'
");
$stmt->execute([$donation_id]);
$don = $stmt->fetch();
if (!$don) exit('Donation not found or not completed.');
$org = $pdo->query("SELECT * FROM org_profile LIMIT 1")->fetch();
// This is a simple HTML certificate that can be printed or saved as PDF by the user
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Donation Certificate - CharityHub</title>
<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=Cinzel:wght@400;700&family=Inter:wght@400;600&display=swap" rel="stylesheet">
<style>
body { background: #f3f4f6; padding: 40px 20px; font-family: 'Inter', sans-serif; }
.certificate-container {
max-width: 800px;
margin: 0 auto;
background: #fff;
padding: 60px;
border: 20px solid #059669;
position: relative;
box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1);
text-align: center;
}
.certificate-container::after {
content: '';
position: absolute;
top: 10px; left: 10px; right: 10px; bottom: 10px;
border: 2px solid #059669;
pointer-events: none;
}
.cert-header { font-family: 'Cinzel', serif; font-size: 3rem; color: #111827; margin-bottom: 10px; }
.cert-sub { font-size: 1.25rem; color: #059669; font-weight: 600; text-transform: uppercase; letter-spacing: 0.1em; margin-bottom: 40px; }
.cert-body { font-size: 1.2rem; color: #4b5563; line-height: 1.8; margin-bottom: 40px; }
.cert-name { font-family: 'Cinzel', serif; font-size: 2.5rem; color: #059669; margin: 20px 0; border-bottom: 2px solid #e5e7eb; display: inline-block; padding: 0 40px; }
.cert-footer { margin-top: 60px; display: flex; justify-content: space-between; align-items: flex-end; }
.signature { border-top: 1px solid #9ca3af; padding-top: 10px; width: 200px; font-style: italic; color: #6b7280; }
.stamp { width: 120px; height: 120px; border: 4px double #059669; border-radius: 50%; display: flex; align-items: center; justify-content: center; color: #059669; font-weight: 800; transform: rotate(-15deg); font-size: 0.8rem; margin: 0 auto; }
@media print {
body { background: #fff; padding: 0; }
.certificate-container { box-shadow: none; border-width: 15px; }
.no-print { display: none; }
}
.no-print { margin-top: 30px; text-align: center; }
.btn-print { background: #059669; color: #fff; border: none; padding: 12px 30px; border-radius: 9999px; font-weight: 600; cursor: pointer; text-decoration: none; }
</style>
</head>
<body>
<div class="certificate-container">
<div class="cert-header">Certificate</div>
<div class="cert-sub">of Appreciation</div>
<div class="cert-body">
This certificate is proudly presented to
<br>
<div class="cert-name"><?= htmlspecialchars($don['donor_name'] ?: 'Valued Donor') ?></div>
<br>
In recognition of their generous donation of <strong>OMR <?= number_format($don['amount'], 3) ?></strong>
<br>
towards the cause: <strong><?= htmlspecialchars($don['case_title']) ?></strong>
</div>
<div class="stamp">
OFFICIAL<br>SEAL<br>CHARITYHUB
</div>
<div class="cert-footer">
<div class="signature">
Date: <?= date('M j, Y', strtotime($don['created_at'])) ?>
</div>
<div class="signature">
Authorized Signature<br>
<?= htmlspecialchars($org['name_en'] ?? 'CharityHub') ?>
</div>
</div>
</div>
<div class="no-print">
<button onclick="window.print()" class="btn-print">Print or Save as PDF</button>
<a href="index.php" style="margin-left: 15px; color: #6b7280; text-decoration: none;">Back to Home</a>
</div>
</body>
</html>

View File

@ -66,12 +66,15 @@ if ($donation) {
}
$success = true;
$final_donation_id = $fullDonation['id'];
} else {
// Check if it was already completed (user refreshed page)
$stmt = $pdo->prepare("SELECT * FROM donations transaction_id = ? AND status = 'completed'");
$stmt = $pdo->prepare("SELECT * FROM donations WHERE transaction_id = ? AND status = 'completed'");
$stmt->execute([$session_id]);
if ($stmt->fetch()) {
$existing = $stmt->fetch();
if ($existing) {
$success = true;
$final_donation_id = $existing['id'];
}
}
?>
@ -82,12 +85,15 @@ if ($donation) {
<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">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.1/font/bootstrap-icons.css">
<style>
body { background-color: #f8fafc; font-family: 'Inter', sans-serif; }
.success-card { max-width: 600px; border-radius: 24px; border: none; overflow: hidden; }
.success-icon { background: #ecfdf5; color: #10b981; width: 100px; height: 100px; border-radius: 50%; display: flex; align-items: center; justify-content: center; margin: 0 auto; }
.btn-home { background: #059669; color: white; border: none; padding: 12px 40px; border-radius: 12px; font-weight: 600; transition: all 0.3s; }
.btn-home:hover { background: #047857; color: white; transform: translateY(-2px); }
.btn-cert { background: #fff; color: #059669; border: 2px solid #059669; padding: 12px 40px; border-radius: 12px; font-weight: 600; transition: all 0.3s; text-decoration: none; display: inline-block; }
.btn-cert:hover { background: #ecfdf5; color: #047857; }
</style>
</head>
<body>
@ -103,6 +109,14 @@ if ($donation) {
</div>
<h1 class="fw-bold mb-3">Thank You!</h1>
<p class="text-muted fs-5 mb-4">Your donation has been successfully processed. Your generosity helps us continue our mission.</p>
<div class="d-flex flex-column flex-sm-row gap-3 justify-content-center mb-5">
<a href="certificate.php?id=<?= $final_donation_id ?>" target="_blank" class="btn-cert">
<i class="bi bi-patch-check me-2"></i>Download Certificate
</a>
<a href="index.php" class="btn btn-home">Return to Home</a>
</div>
<div class="bg-light p-4 rounded-4 mb-4 text-start">
<div class="d-flex justify-content-between mb-2">
<span class="text-muted">Transaction ID</span>
@ -113,8 +127,7 @@ if ($donation) {
<span class="badge bg-success rounded-pill px-3">Completed</span>
</div>
</div>
<p class="small text-muted mb-4">Confirmation messages have been sent to the relevant parties.</p>
<a href="index.php" class="btn btn-home">Return to Home</a>
<p class="small text-muted mb-0">Confirmation messages have been sent to the relevant parties.</p>
</div>
<?php else: ?>
<div class="card border-0 shadow-lg p-5 rounded-4">