Skip to content

Latest commit

Β 

History

History
626 lines (528 loc) Β· 16.2 KB

File metadata and controls

626 lines (528 loc) Β· 16.2 KB

Admin Reports System Documentation

Overview

The Admin Reports System provides comprehensive analytics and insights for administrators to monitor and manage the Generous Hands donation platform. The system includes 5 main report categories that cover all aspects of platform operations.


πŸ“Š Available Reports

1. Donation Overview Report

Endpoint: GET /api/admin/reports/donation-overview

Purpose: Comprehensive analysis of donation patterns, trends, and performance metrics.

Key Metrics:

  • Total donations and confirmation rates
  • Status breakdown (submitted, assigned, picked_up, delivered, confirmed, cancelled)
  • Urgency level distribution
  • Top performing charities
  • Most donated categories
  • Daily donation trends (30-day chart data)
  • Average confirmation time

Query Parameters:

  • startDate (optional): Start date for custom date range
  • endDate (optional): End date for custom date range
  • period (optional): Predefined periods - 7d, 30d, 90d, 1y, all (default: 30d)

2. User Activity Report

Endpoint: GET /api/admin/reports/user-activity

Purpose: Monitor user registration trends, verification status, and platform engagement.

Key Metrics:

  • Total users by time period
  • User breakdown by role (donor, volunteer, charity, admin)
  • Verification statistics for volunteers and charities
  • Registration trends over time
  • Active users (recent donations/pickup activity)

Query Parameters:

  • period (optional): Time period - 7d, 30d, 90d, 1y, all (default: 30d)

3. Charity Performance Report

Endpoint: GET /api/admin/reports/charity-performance

Purpose: Analyze charity engagement, response times, and service quality.

Key Metrics:

  • Charity overview with donation counts and confirmation rates
  • Average response times for donation confirmations
  • Needs fulfillment analysis (how well donations match charity needs)
  • Thank you note completion rates
  • Overall charity performance summary

4. Volunteer Efficiency Report

Endpoint: GET /api/admin/reports/volunteer-efficiency

Purpose: Track volunteer performance, pickup completion rates, and delivery efficiency.

Key Metrics:

  • Volunteer overview with assignment and completion statistics
  • Pickup status breakdown across all volunteers
  • Average delivery times by volunteer
  • Top performing volunteers
  • Overall volunteer efficiency metrics

5. System Health Report

Endpoint: GET /api/admin/reports/system-health

Purpose: Monitor data quality, identify system issues, and track pending administrative actions.

Key Metrics:

  • Data quality issues (missing coordinates, incomplete profiles)
  • Incomplete donation flows (stuck submissions, delayed deliveries)
  • Pending administrative actions (verifications, incomplete categories)
  • System overview statistics
  • Overall health score (0-100)

πŸ” Authentication

All admin reports require:

  1. Valid JWT token in Authorization header
  2. User must have admin role
Authorization: Bearer <admin_jwt_token>

πŸ“‹ Sample API Requests & Responses

Donation Overview Report

GET /api/admin/reports/donation-overview?period=30d
Authorization: Bearer <admin_token>

Response:

{
  "success": true,
  "data": {
    "summary": {
      "totalDonations": 156,
      "confirmedDonations": 142,
      "confirmationRate": "91.0",
      "avgConfirmationTimeHours": 18
    },
    "statusBreakdown": [
      { "_id": "confirmed", "count": 142 },
      { "_id": "delivered", "count": 8 },
      { "_id": "picked_up", "count": 4 },
      { "_id": "assigned", "count": 2 }
    ],
    "urgencyBreakdown": [
      { "_id": "medium", "count": 89 },
      { "_id": "high", "count": 45 },
      { "_id": "low", "count": 22 }
    ],
    "topCharities": [
      { "charityName": "Food Bank Central", "count": 34 },
      { "charityName": "Clothes for All", "count": 28 }
    ],
    "categoryBreakdown": [
      { "categoryName": "Food items", "count": 67 },
      { "categoryName": "Clothing", "count": 45 }
    ],
    "dailyTrends": [
      { "_id": "2024-01-01", "count": 5 },
      { "_id": "2024-01-02", "count": 8 }
    ],
    "reportPeriod": {
      "startDate": "2023-12-02T00:00:00.000Z",
      "endDate": "Present",
      "period": "30d"
    }
  }
}

System Health Report

GET /api/admin/reports/system-health
Authorization: Bearer <admin_token>

Response:

{
  "success": true,
  "data": {
    "dataQuality": {
      "usersWithMissingData": 3,
      "donationsWithoutCoordinates": 0,
      "charitiesWithoutLocation": 1
    },
    "incompleteFlows": {
      "stuckSubmissions": 2,
      "stuckPickups": 1,
      "unconfirmedDeliveries": 5
    },
    "pendingActions": {
      "pendingVerifications": 8,
      "categoriesNeedingDescription": 2
    },
    "systemOverview": {
      "totalUsers": 245,
      "totalDonations": 156,
      "totalPickupRequests": 134,
      "totalCategories": 12
    },
    "healthScore": 87
  }
}

πŸ–₯️ Frontend Implementation Examples

React Admin Dashboard Component

import React, { useState, useEffect } from 'react';
import { BarChart, Bar, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer, PieChart, Pie, Cell } from 'recharts';

const AdminDashboard = () => {
  const [reports, setReports] = useState({
    donationOverview: null,
    userActivity: null,
    charityPerformance: null,
    volunteerEfficiency: null,
    systemHealth: null
  });
  const [loading, setLoading] = useState(true);
  const [selectedPeriod, setSelectedPeriod] = useState('30d');

  useEffect(() => {
    fetchAllReports();
  }, [selectedPeriod]);

  const fetchAllReports = async () => {
    setLoading(true);
    try {
      const [donation, user, charity, volunteer, system] = await Promise.all([
        fetchReport('donation-overview', { period: selectedPeriod }),
        fetchReport('user-activity', { period: selectedPeriod }),
        fetchReport('charity-performance'),
        fetchReport('volunteer-efficiency'),
        fetchReport('system-health')
      ]);

      setReports({
        donationOverview: donation,
        userActivity: user,
        charityPerformance: charity,
        volunteerEfficiency: volunteer,
        systemHealth: system
      });
    } catch (error) {
      console.error('Error fetching reports:', error);
    } finally {
      setLoading(false);
    }
  };

  const fetchReport = async (reportType, params = {}) => {
    const queryString = new URLSearchParams(params).toString();
    const response = await fetch(`/api/admin/reports/${reportType}?${queryString}`, {
      headers: {
        'Authorization': `Bearer ${authToken}`,
        'Content-Type': 'application/json'
      }
    });
    const data = await response.json();
    return data.success ? data.data : null;
  };

  if (loading) return <div className="loading">Loading admin dashboard...</div>;

  return (
    <div className="admin-dashboard">
      {/* Header with Period Selector */}
      <div className="dashboard-header">
        <h1>Admin Dashboard</h1>
        <select
          value={selectedPeriod}
          onChange={(e) => setSelectedPeriod(e.target.value)}
          className="period-selector"
        >
          <option value="7d">Last 7 Days</option>
          <option value="30d">Last 30 Days</option>
          <option value="90d">Last 90 Days</option>
          <option value="1y">Last Year</option>
          <option value="all">All Time</option>
        </select>
      </div>

      {/* Key Metrics Cards */}
      <div className="metrics-grid">
        <MetricCard
          title="Total Donations"
          value={reports.donationOverview?.summary.totalDonations || 0}
          subtitle={`${reports.donationOverview?.summary.confirmationRate || 0}% confirmed`}
          color="blue"
        />
        <MetricCard
          title="Active Users"
          value={reports.userActivity?.summary.totalUsers || 0}
          subtitle={`${reports.userActivity?.summary.pendingVerifications || 0} pending verification`}
          color="green"
        />
        <MetricCard
          title="System Health"
          value={`${reports.systemHealth?.healthScore || 0}%`}
          subtitle="Overall platform health"
          color={getHealthColor(reports.systemHealth?.healthScore)}
        />
        <MetricCard
          title="Avg Response Time"
          value={`${reports.charityPerformance?.summary.avgResponseTimeHours || 0}h`}
          subtitle="Charity response time"
          color="purple"
        />
      </div>

      {/* Charts Section */}
      <div className="charts-section">
        {/* Donation Trends Chart */}
        <div className="chart-container">
          <h3>Donation Trends</h3>
          <ResponsiveContainer width="100%" height={300}>
            <BarChart data={reports.donationOverview?.dailyTrends || []}>
              <CartesianGrid strokeDasharray="3 3" />
              <XAxis dataKey="_id" />
              <YAxis />
              <Tooltip />
              <Bar dataKey="count" fill="#3b82f6" />
            </BarChart>
          </ResponsiveContainer>
        </div>

        {/* Status Distribution */}
        <div className="chart-container">
          <h3>Donation Status Distribution</h3>
          <ResponsiveContainer width="100%" height={300}>
            <PieChart>
              <Pie
                data={reports.donationOverview?.statusBreakdown || []}
                dataKey="count"
                nameKey="_id"
                cx="50%"
                cy="50%"
                outerRadius={80}
                fill="#8884d8"
                label
              >
                {(reports.donationOverview?.statusBreakdown || []).map((entry, index) => (
                  <Cell key={`cell-${index}`} fill={getStatusColor(entry._id)} />
                ))}
              </Pie>
              <Tooltip />
            </PieChart>
          </ResponsiveContainer>
        </div>
      </div>

      {/* Detailed Reports Section */}
      <div className="reports-section">
        <ReportTabs reports={reports} />
      </div>
    </div>
  );
};

const MetricCard = ({ title, value, subtitle, color }) => (
  <div className={`metric-card ${color}`}>
    <h3>{title}</h3>
    <div className="metric-value">{value}</div>
    <div className="metric-subtitle">{subtitle}</div>
  </div>
);

const ReportTabs = ({ reports }) => {
  const [activeTab, setActiveTab] = useState('donations');

  const tabs = [
    { id: 'donations', label: 'Donations', data: reports.donationOverview },
    { id: 'users', label: 'Users', data: reports.userActivity },
    { id: 'charities', label: 'Charities', data: reports.charityPerformance },
    { id: 'volunteers', label: 'Volunteers', data: reports.volunteerEfficiency },
    { id: 'system', label: 'System Health', data: reports.systemHealth }
  ];

  return (
    <div className="report-tabs">
      <div className="tab-headers">
        {tabs.map(tab => (
          <button
            key={tab.id}
            className={`tab-header ${activeTab === tab.id ? 'active' : ''}`}
            onClick={() => setActiveTab(tab.id)}
          >
            {tab.label}
          </button>
        ))}
      </div>

      <div className="tab-content">
        {tabs.find(tab => tab.id === activeTab)?.data && (
          <ReportContent
            type={activeTab}
            data={tabs.find(tab => tab.id === activeTab).data}
          />
        )}
      </div>
    </div>
  );
};

// Helper functions
const getHealthColor = (score) => {
  if (score >= 80) return 'green';
  if (score >= 60) return 'yellow';
  return 'red';
};

const getStatusColor = (status) => {
  const colors = {
    confirmed: '#10b981',
    delivered: '#3b82f6',
    picked_up: '#f59e0b',
    assigned: '#8b5cf6',
    submitted: '#6b7280',
    cancelled: '#ef4444'
  };
  return colors[status] || '#6b7280';
};

export default AdminDashboard;

CSS Styling

.admin-dashboard {
  padding: 24px;
  max-width: 1400px;
  margin: 0 auto;
}

.dashboard-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 32px;
}

.dashboard-header h1 {
  font-size: 2rem;
  font-weight: bold;
  color: #1f2937;
}

.period-selector {
  padding: 8px 16px;
  border: 1px solid #d1d5db;
  border-radius: 6px;
  background: white;
  font-size: 14px;
}

.metrics-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
  gap: 20px;
  margin-bottom: 32px;
}

.metric-card {
  background: white;
  padding: 24px;
  border-radius: 12px;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
  border-left: 4px solid;
}

.metric-card.blue { border-left-color: #3b82f6; }
.metric-card.green { border-left-color: #10b981; }
.metric-card.yellow { border-left-color: #f59e0b; }
.metric-card.red { border-left-color: #ef4444; }
.metric-card.purple { border-left-color: #8b5cf6; }

.metric-card h3 {
  font-size: 0.875rem;
  font-weight: 500;
  color: #6b7280;
  margin: 0 0 8px 0;
}

.metric-value {
  font-size: 2rem;
  font-weight: bold;
  color: #1f2937;
  margin-bottom: 4px;
}

.metric-subtitle {
  font-size: 0.75rem;
  color: #9ca3af;
}

.charts-section {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(400px, 1fr));
  gap: 24px;
  margin-bottom: 32px;
}

.chart-container {
  background: white;
  padding: 24px;
  border-radius: 12px;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
}

.chart-container h3 {
  margin: 0 0 16px 0;
  font-size: 1.125rem;
  font-weight: 600;
  color: #1f2937;
}

.reports-section {
  background: white;
  border-radius: 12px;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
  overflow: hidden;
}

.tab-headers {
  display: flex;
  border-bottom: 1px solid #e5e7eb;
}

.tab-header {
  padding: 16px 24px;
  border: none;
  background: none;
  cursor: pointer;
  font-weight: 500;
  color: #6b7280;
  transition: all 0.2s;
}

.tab-header:hover {
  color: #3b82f6;
  background: #f9fafb;
}

.tab-header.active {
  color: #3b82f6;
  border-bottom: 2px solid #3b82f6;
}

.tab-content {
  padding: 24px;
}

.loading {
  display: flex;
  justify-content: center;
  align-items: center;
  height: 400px;
  font-size: 1.125rem;
  color: #6b7280;
}

πŸ”§ Implementation Tips

1. Caching Strategy

// Implement Redis caching for expensive aggregation queries
const cacheKey = `admin_report_${reportType}_${JSON.stringify(params)}`;
const cachedResult = await redis.get(cacheKey);

if (cachedResult) {
    return JSON.parse(cachedResult);
}

// Generate report...
await redis.setex(cacheKey, 300, JSON.stringify(report)); // 5 min cache

2. Performance Optimization

  • Use MongoDB aggregation pipelines for complex queries
  • Implement database indexes on frequently queried fields
  • Add pagination for large datasets
  • Use parallel Promise.all() for independent queries

3. Real-time Updates

// WebSocket integration for real-time dashboard updates
const socket = io();

socket.on('donation_status_changed', (data) => {
    // Update relevant metrics without full refresh
    updateDonationMetrics(data);
});

4. Export Functionality

const exportReport = async (reportType, format = 'csv') => {
    const data = await fetchReport(reportType);

    if (format === 'csv') {
        return convertToCSV(data);
    } else if (format === 'pdf') {
        return generatePDF(data);
    }
};

πŸš€ Advanced Features

1. Custom Date Ranges

Allow admins to select custom date ranges for more specific analysis.

2. Automated Alerts

Set up automated alerts for:

  • System health score drops below threshold
  • Unusual donation patterns
  • High number of pending verifications

3. Comparative Analysis

Compare metrics across different time periods to identify trends.

4. Role-based Report Access

Different report sections for different admin levels.


πŸ“ˆ Business Impact

These reports enable administrators to:

  1. Monitor Platform Health - Identify and resolve issues quickly
  2. Optimize Operations - Improve volunteer and charity performance
  3. Make Data-Driven Decisions - Use metrics to guide platform improvements
  4. Ensure Quality - Maintain high standards across all user interactions
  5. Scale Effectively - Understand growth patterns and resource needs

The comprehensive reporting system provides the insights needed to run a successful donation platform and maximize positive community impact.