From 699f776eee4e5a603a5b55eaec236260dfc1ff01 Mon Sep 17 00:00:00 2001 From: Flatlogic Bot Date: Fri, 13 Feb 2026 09:21:53 +0000 Subject: [PATCH] add financial summery --- admin/audit_logs.php | 80 ++++++++++ admin/auth.php | 14 +- admin/cases.php | 45 +++++- admin/donations.php | 8 +- admin/donors.php | 88 +++++++++++ admin/export_donations.php | 47 ++++++ admin/financial_summary.php | 281 ++++++++++++++++++++++++++++++++++++ admin/index.php | 76 ++++++++++ admin/login.php | 3 + admin/sidebar.php | 7 +- certificate.php | 106 ++++++++++++++ success.php | 21 ++- 12 files changed, 766 insertions(+), 10 deletions(-) create mode 100644 admin/audit_logs.php create mode 100644 admin/donors.php create mode 100644 admin/export_donations.php create mode 100644 admin/financial_summary.php create mode 100644 certificate.php diff --git a/admin/audit_logs.php b/admin/audit_logs.php new file mode 100644 index 0000000..0380ac3 --- /dev/null +++ b/admin/audit_logs.php @@ -0,0 +1,80 @@ +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(); +?> + + + + + + Audit Logs - CharityHub Admin + + + + + + + +
+
+

Activity Audit Logs

+
+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + +
UserActionDetailsDate
+ + + +
No logs found.
+
+
+
+ + \ No newline at end of file diff --git a/admin/auth.php b/admin/auth.php index 48bf5e2..5ab6348 100644 --- a/admin/auth.php +++ b/admin/auth.php @@ -43,4 +43,16 @@ function get_user() { function is_super_admin() { $user = get_user(); return $user && $user['role'] === 'super_admin'; -} \ No newline at end of file +} + +/** + * 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]); +} diff --git a/admin/cases.php b/admin/cases.php index 32f3571..3a2c069 100644 --- a/admin/cases.php +++ b/admin/cases.php @@ -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; diff --git a/admin/donations.php b/admin/donations.php index 0ed9899..f9489ad 100644 --- a/admin/donations.php +++ b/admin/donations.php @@ -40,7 +40,10 @@ $donations = $pdo->query("SELECT d.*, c.title_en as case_title, cat.name_en as c
-

Donations History

+

Donations History

+ + Export to CSV +
@@ -61,6 +64,9 @@ $donations = $pdo->query("SELECT d.*, c.title_en as case_title, cat.name_en as c + + Gift +
diff --git a/admin/donors.php b/admin/donors.php new file mode 100644 index 0000000..319de5c --- /dev/null +++ b/admin/donors.php @@ -0,0 +1,88 @@ +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(); +?> + + + + + + Donors CRM - CharityHub Admin + + + + + + + +
+
+

Donor CRM

+
+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Donor NameEmailPhoneDonationsTotal AmountLast Donation
+ + OMR
No donors found with completed payments.
+
+
+
+ + \ No newline at end of file diff --git a/admin/export_donations.php b/admin/export_donations.php new file mode 100644 index 0000000..b70d820 --- /dev/null +++ b/admin/export_donations.php @@ -0,0 +1,47 @@ +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; diff --git a/admin/financial_summary.php b/admin/financial_summary.php new file mode 100644 index 0000000..f09d674 --- /dev/null +++ b/admin/financial_summary.php @@ -0,0 +1,281 @@ +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']; + } +} + +?> + + + + + + Financial Summary - CharityHub Admin + + + + + + + + +
+
+
+

Financial Summary

+

Detailed analysis of donations and revenue streams.

+
+
+
+ + +
+
+
+
Total Revenue
+
OMR
+
From donations
+
+
+
+
+
Avg. Donation
+
OMR
+
Per completed donation
+
+
+
+
+
Pending Revenue
+
+
Awaiting payment
+
+
+
+
+
Total Donations
+
+
All statuses included
+
+
+
+ +
+ +
+
+
Monthly Revenue Trend (Last 12 Months)
+
+ +
+
+
+ +
+
+
Revenue by Category
+
+ +
+
+
+
+ +
+ +
+
+
Top Performing Cases
+
+ +
+
+ + OMR +
+ 0 ? ($case['total'] / $case['goal']) * 100 : 0; + ?> +
+
+
+
% of OMR goal
+
+ +
+
+
+ +
+
+
Regular vs Gift Donations (Revenue)
+
+ +
+
+
+
Regular
+
OMR
+
+
+
Gift
+
OMR
+
+
+
+
+
+
+ + + + diff --git a/admin/index.php b/admin/index.php index c92e5b2..a3cafbc 100644 --- a/admin/index.php +++ b/admin/index.php @@ -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(" Dashboard - CharityHub Admin + @@ -92,6 +110,27 @@ $recent_donations = $pdo->query("
+
+
+
+
Donation Trends (Last 30 Days)
+
+ +
+
+
+ +
+
Recent Donations
@@ -136,5 +175,42 @@ $recent_donations = $pdo->query("
+ + \ No newline at end of file diff --git a/admin/login.php b/admin/login.php index 5905eca..706258b 100644 --- a/admin/login.php +++ b/admin/login.php @@ -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; } diff --git a/admin/sidebar.php b/admin/sidebar.php index b81d7df..fc76234 100644 --- a/admin/sidebar.php +++ b/admin/sidebar.php @@ -10,7 +10,10 @@ $current_page = basename($_SERVER['PHP_SELF']); Categories Cases Donations + Financial Summary + Donors (CRM) + Audit Logs
- Logout + Logout - + \ No newline at end of file diff --git a/certificate.php b/certificate.php new file mode 100644 index 0000000..195e389 --- /dev/null +++ b/certificate.php @@ -0,0 +1,106 @@ +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 +?> + + + + + + Donation Certificate - CharityHub + + + + + + + +
+
Certificate
+
of Appreciation
+ +
+ This certificate is proudly presented to +
+
+
+ In recognition of their generous donation of OMR +
+ towards the cause: +
+ +
+ OFFICIAL
SEAL
CHARITYHUB +
+ + +
+ +
+ + Back to Home +
+ + + \ No newline at end of file diff --git a/success.php b/success.php index b95008b..d483bc8 100644 --- a/success.php +++ b/success.php @@ -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) { Donation Successful - CharityHub + @@ -103,6 +109,14 @@ if ($donation) {

Thank You!

Your donation has been successfully processed. Your generosity helps us continue our mission.

+ +
+ + Download Certificate + + Return to Home +
+
Transaction ID @@ -113,8 +127,7 @@ if ($donation) { Completed
-

Confirmation messages have been sent to the relevant parties.

- Return to Home +

Confirmation messages have been sent to the relevant parties.