feat: Add comprehensive documentation suite and reorganize project structure

- Created complete documentation in docs/ directory
- Added PROJECT_OVERVIEW.md with feature highlights and getting started guide
- Added ARCHITECTURE.md with system design and technical details
- Added SECURITY.md with comprehensive security implementation guide
- Added DEVELOPMENT.md with development workflows and best practices
- Added DEPLOYMENT.md with production deployment instructions
- Added API.md with complete REST API documentation
- Added CONTRIBUTING.md with contribution guidelines
- Added CHANGELOG.md with version history and migration notes
- Reorganized all documentation files into docs/ directory for better organization
- Updated README.md with proper documentation links and quick navigation
- Enhanced project structure with professional documentation standards
This commit is contained in:
SamiAhmed7777
2025-10-21 00:39:45 -07:00
commit 0b7e2d0a5b
6080 changed files with 1332936 additions and 0 deletions

View File

@@ -0,0 +1,151 @@
<?php
$title = "Donation Analytics";
include_once '../../../f_core/header.php';
use Donations\AnalyticsHandler;
$analytics_handler = new AnalyticsHandler();
$summary = $analytics_handler->getSummary($streamer_id);
$trends = $analytics_handler->getTrends($streamer_id);
$top_donors = $analytics_handler->getTopDonors($streamer_id);
?>
<div class="container mt-4">
<h2>Donation Analytics</h2>
<!-- Summary Cards -->
<div class="row mb-4">
<div class="col-md-3">
<div class="card">
<div class="card-body">
<h5 class="card-title">Total Donations</h5>
<h2 class="card-text"><?php echo number_format($summary['total_donations']); ?></h2>
</div>
</div>
</div>
<div class="col-md-3">
<div class="card">
<div class="card-body">
<h5 class="card-title">Total Amount</h5>
<h2 class="card-text">$<?php echo number_format($summary['total_amount'], 2); ?></h2>
</div>
</div>
</div>
<div class="col-md-3">
<div class="card">
<div class="card-body">
<h5 class="card-title">Average Donation</h5>
<h2 class="card-text">$<?php echo number_format($summary['average_donation'], 2); ?></h2>
</div>
</div>
</div>
<div class="col-md-3">
<div class="card">
<div class="card-body">
<h5 class="card-title">Unique Donors</h5>
<h2 class="card-text"><?php echo number_format($summary['unique_donors']); ?></h2>
</div>
</div>
</div>
</div>
<!-- Trends Chart -->
<div class="card mb-4">
<div class="card-body">
<h5 class="card-title">Donation Trends</h5>
<canvas id="trendsChart"></canvas>
</div>
</div>
<!-- Top Donors -->
<div class="card">
<div class="card-body">
<h5 class="card-title">Top Donors</h5>
<div class="table-responsive">
<table class="table">
<thead>
<tr>
<th>Donor</th>
<th>Donations</th>
<th>Total Amount</th>
</tr>
</thead>
<tbody>
<?php foreach ($top_donors as $donor): ?>
<tr>
<td><?php echo htmlspecialchars($donor['display_name']); ?></td>
<td><?php echo number_format($donor['donation_count']); ?></td>
<td>$<?php echo number_format($donor['total_amount'], 2); ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script>
document.addEventListener('DOMContentLoaded', function() {
// Prepare data for the trends chart
const trendsData = <?php echo json_encode($trends); ?>;
const dates = trendsData.map(item => item.date);
const amounts = trendsData.map(item => item.total);
const counts = trendsData.map(item => item.count);
// Create trends chart
const ctx = document.getElementById('trendsChart').getContext('2d');
new Chart(ctx, {
type: 'line',
data: {
labels: dates,
datasets: [{
label: 'Daily Amount',
data: amounts,
borderColor: 'rgb(75, 192, 192)',
tension: 0.1,
yAxisID: 'y'
}, {
label: 'Number of Donations',
data: counts,
borderColor: 'rgb(255, 99, 132)',
tension: 0.1,
yAxisID: 'y1'
}]
},
options: {
responsive: true,
interaction: {
mode: 'index',
intersect: false,
},
scales: {
y: {
type: 'linear',
display: true,
position: 'left',
title: {
display: true,
text: 'Amount ($)'
}
},
y1: {
type: 'linear',
display: true,
position: 'right',
title: {
display: true,
text: 'Number of Donations'
},
grid: {
drawOnChartArea: false
}
}
}
}
});
});
</script>
<?php include_once '../../../f_core/footer.php'; ?>

View File

@@ -0,0 +1,189 @@
<?php
$title = "Donate to {$streamer['display_name']}";
include_once '../../../f_core/header.php';
?>
<div class="container mt-5">
<div class="row justify-content-center">
<div class="col-md-6">
<div class="card">
<div class="card-header">
<h3 class="card-title">Donate to <?php echo htmlspecialchars($streamer['display_name']); ?></h3>
</div>
<div class="card-body">
<form id="donation-form">
<input type="hidden" name="streamer_id" value="<?php echo $streamer['user_id']; ?>">
<div class="mb-3">
<label class="form-label">Donation Amount</label>
<div class="input-group">
<span class="input-group-text">$</span>
<input type="number"
class="form-control"
name="amount"
min="<?php echo $config['min_donation']; ?>"
max="<?php echo $config['max_donation']; ?>"
step="0.01"
required>
</div>
<div class="form-text">
Minimum: $<?php echo number_format($config['min_donation'], 2); ?><br>
Maximum: $<?php echo number_format($config['max_donation'], 2); ?>
</div>
</div>
<div class="mb-3">
<label class="form-label">Quick Select Amount</label>
<div class="btn-group w-100">
<?php foreach ($config['default_amounts'] as $amount): ?>
<button type="button"
class="btn btn-outline-primary quick-amount"
data-amount="<?php echo $amount; ?>">
$<?php echo number_format($amount, 2); ?>
</button>
<?php endforeach; ?>
</div>
</div>
<div class="mb-3">
<label class="form-label">Message (Optional)</label>
<textarea class="form-control"
name="message"
rows="3"
maxlength="200"></textarea>
<div class="form-text">Maximum 200 characters</div>
</div>
<div class="d-grid">
<button type="submit" class="btn btn-primary">Donate Now</button>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
<!-- Square Payment Form -->
<div id="payment-form-container" style="display: none;">
<div id="payment-form"></div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
const form = document.getElementById('donation-form');
const amountInput = form.querySelector('input[name="amount"]');
const quickAmountButtons = document.querySelectorAll('.quick-amount');
const paymentFormContainer = document.getElementById('payment-form-container');
const paymentForm = document.getElementById('payment-form');
// Handle quick amount selection
quickAmountButtons.forEach(button => {
button.addEventListener('click', function() {
amountInput.value = this.dataset.amount;
amountInput.dispatchEvent(new Event('change'));
});
});
// Handle form submission
form.addEventListener('submit', async function(e) {
e.preventDefault();
const formData = new FormData(form);
try {
const response = await fetch('process_donation.php', {
method: 'POST',
body: formData
});
const result = await response.json();
if (result.success) {
// Initialize Square payment form
const paymentForm = new SqPaymentForm({
applicationId: '<?php echo $config['application_id']; ?>',
locationId: '<?php echo $config['location_id']; ?>',
inputClass: 'sq-input',
inputStyles: [{
fontSize: '16px',
fontFamily: 'Helvetica Neue',
padding: '16px',
color: '#373F4A',
backgroundColor: 'transparent',
lineHeight: '20px',
placeholderColor: '#999',
_webkitFontSmoothing: 'antialiased',
_mozOsxFontSmoothing: 'grayscale'
}],
cardNumber: {
elementId: 'sq-card-number',
placeholder: '•••• •••• •••• ••••'
},
cvv: {
elementId: 'sq-cvv',
placeholder: 'CVV'
},
expirationDate: {
elementId: 'sq-expiration-date',
placeholder: 'MM/YY'
},
postalCode: {
elementId: 'sq-postal-code',
placeholder: 'Postal'
},
callbacks: {
cardNonceResponseReceived: function(err, nonce, cardData) {
if (err) {
console.error('Error generating card nonce:', err);
alert('Error processing payment. Please try again.');
return;
}
// Send payment to server
fetch('process_payment.php', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
nonce: nonce,
amount: formData.get('amount'),
streamer_id: formData.get('streamer_id'),
message: formData.get('message')
})
})
.then(response => response.json())
.then(result => {
if (result.success) {
alert('Thank you for your donation!');
window.location.reload();
} else {
alert(result.error || 'Error processing payment. Please try again.');
}
})
.catch(error => {
console.error('Error:', error);
alert('Error processing payment. Please try again.');
});
},
unsupportedBrowserDetected: function() {
alert('Your browser is not supported. Please use a modern browser.');
}
}
});
// Show payment form
paymentFormContainer.style.display = 'block';
paymentForm.build();
} else {
alert(result.error || 'Error processing donation. Please try again.');
}
} catch (error) {
console.error('Error:', error);
alert('Error processing donation. Please try again.');
}
});
});
</script>
<?php include_once '../../../f_core/footer.php'; ?>

View File

@@ -0,0 +1,194 @@
<?php
$title = "Donation Goals";
include_once '../../../f_core/header.php';
use Donations\GoalHandler;
$goal_handler = new GoalHandler();
$active_goals = $goal_handler->getActiveGoals($streamer_id);
$all_goals = $goal_handler->getStreamerGoals($streamer_id);
?>
<div class="container mt-4">
<div class="d-flex justify-content-between align-items-center mb-4">
<h2>Donation Goals</h2>
<button type="button" class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#createGoalModal">
Create New Goal
</button>
</div>
<!-- Active Goals -->
<div class="row mb-4">
<div class="col-12">
<h3>Active Goals</h3>
</div>
<?php foreach ($active_goals as $goal): ?>
<?php
$progress = ($goal['current_amount'] / $goal['target_amount']) * 100;
$milestones = $goal_handler->getGoalMilestones($goal['goal_id']);
?>
<div class="col-md-6 mb-4">
<div class="card">
<div class="card-body">
<h5 class="card-title"><?php echo htmlspecialchars($goal['title']); ?></h5>
<p class="card-text"><?php echo htmlspecialchars($goal['description']); ?></p>
<!-- Progress Bar -->
<div class="progress mb-3">
<div class="progress-bar" role="progressbar"
style="width: <?php echo $progress; ?>%"
aria-valuenow="<?php echo $progress; ?>"
aria-valuemin="0"
aria-valuemax="100">
<?php echo number_format($progress, 1); ?>%
</div>
</div>
<!-- Amount Info -->
<div class="d-flex justify-content-between mb-3">
<span>$<?php echo number_format($goal['current_amount'], 2); ?> raised</span>
<span>Goal: $<?php echo number_format($goal['target_amount'], 2); ?></span>
</div>
<!-- Milestones -->
<?php if (!empty($milestones)): ?>
<h6>Milestones</h6>
<div class="list-group">
<?php foreach ($milestones as $milestone): ?>
<?php
$milestone_progress = ($goal['current_amount'] / $milestone['target_amount']) * 100;
$milestone_progress = min($milestone_progress, 100);
?>
<div class="list-group-item">
<div class="d-flex justify-content-between align-items-center">
<div>
<h6 class="mb-1"><?php echo htmlspecialchars($milestone['title']); ?></h6>
<small><?php echo htmlspecialchars($milestone['description']); ?></small>
</div>
<span class="badge <?php echo $milestone['is_achieved'] ? 'bg-success' : 'bg-secondary'; ?>">
<?php echo $milestone['is_achieved'] ? 'Achieved' : 'In Progress'; ?>
</span>
</div>
<div class="progress mt-2">
<div class="progress-bar" role="progressbar"
style="width: <?php echo $milestone_progress; ?>%"
aria-valuenow="<?php echo $milestone_progress; ?>"
aria-valuemin="0"
aria-valuemax="100">
$<?php echo number_format($goal['current_amount'], 2); ?> /
$<?php echo number_format($milestone['target_amount'], 2); ?>
</div>
</div>
</div>
<?php endforeach; ?>
</div>
<?php endif; ?>
</div>
</div>
</div>
<?php endforeach; ?>
</div>
<!-- Completed Goals -->
<div class="row">
<div class="col-12">
<h3>Completed Goals</h3>
</div>
<?php foreach ($all_goals as $goal): ?>
<?php if ($goal['status'] === 'completed'): ?>
<div class="col-md-6 mb-4">
<div class="card">
<div class="card-body">
<h5 class="card-title"><?php echo htmlspecialchars($goal['title']); ?></h5>
<p class="card-text"><?php echo htmlspecialchars($goal['description']); ?></p>
<div class="progress mb-3">
<div class="progress-bar bg-success" role="progressbar"
style="width: 100%"
aria-valuenow="100"
aria-valuemin="0"
aria-valuemax="100">
100%
</div>
</div>
<div class="d-flex justify-content-between">
<span>Completed on <?php echo date('M d, Y', strtotime($goal['updated_at'])); ?></span>
<span>Total: $<?php echo number_format($goal['current_amount'], 2); ?></span>
</div>
</div>
</div>
</div>
<?php endif; ?>
<?php endforeach; ?>
</div>
</div>
<!-- Create Goal Modal -->
<div class="modal fade" id="createGoalModal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Create New Goal</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<form id="createGoalForm">
<div class="mb-3">
<label class="form-label">Title</label>
<input type="text" class="form-control" name="title" required>
</div>
<div class="mb-3">
<label class="form-label">Description</label>
<textarea class="form-control" name="description" rows="3"></textarea>
</div>
<div class="mb-3">
<label class="form-label">Target Amount</label>
<div class="input-group">
<span class="input-group-text">$</span>
<input type="number" class="form-control" name="target_amount"
min="1" step="0.01" required>
</div>
</div>
<div class="mb-3">
<label class="form-label">End Date (Optional)</label>
<input type="date" class="form-control" name="end_date">
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-primary" id="createGoalBtn">Create Goal</button>
</div>
</div>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
const createGoalForm = document.getElementById('createGoalForm');
const createGoalBtn = document.getElementById('createGoalBtn');
createGoalBtn.addEventListener('click', async function() {
const formData = new FormData(createGoalForm);
try {
const response = await fetch('create_goal.php', {
method: 'POST',
body: formData
});
const result = await response.json();
if (result.success) {
window.location.reload();
} else {
alert(result.error || 'Error creating goal. Please try again.');
}
} catch (error) {
console.error('Error:', error);
alert('Error creating goal. Please try again.');
}
});
});
</script>
<?php include_once '../../../f_core/footer.php'; ?>

View File

@@ -0,0 +1,125 @@
<?php
$title = "Donation Notifications";
include_once '../../../f_core/header.php';
use Donations\NotificationHandler;
$notification_handler = new NotificationHandler();
$notifications = $notification_handler->getAllNotifications($streamer_id);
$unread_count = $notification_handler->getUnreadCount($streamer_id);
?>
<div class="container mt-4">
<div class="d-flex justify-content-between align-items-center mb-4">
<h2>Donation Notifications</h2>
<?php if ($unread_count > 0): ?>
<button type="button" class="btn btn-primary" id="markAllReadBtn">
Mark All as Read
</button>
<?php endif; ?>
</div>
<!-- Notifications List -->
<div class="card">
<div class="card-body">
<?php if (empty($notifications)): ?>
<div class="text-center py-5">
<h5>No notifications yet</h5>
<p class="text-muted">You'll see notifications here when you receive donations, achieve goals, or reach milestones.</p>
</div>
<?php else: ?>
<div class="list-group">
<?php foreach ($notifications as $notification): ?>
<div class="list-group-item <?php echo $notification['is_read'] ? '' : 'list-group-item-primary'; ?>"
data-notification-id="<?php echo $notification['notification_id']; ?>">
<div class="d-flex justify-content-between align-items-center">
<div>
<h6 class="mb-1"><?php echo htmlspecialchars($notification['title']); ?></h6>
<p class="mb-1"><?php echo htmlspecialchars($notification['message']); ?></p>
<small class="text-muted">
<?php echo date('M d, Y H:i', strtotime($notification['created_at'])); ?>
</small>
</div>
<?php if (!$notification['is_read']): ?>
<button type="button" class="btn btn-sm btn-outline-primary mark-read-btn">
Mark as Read
</button>
<?php endif; ?>
</div>
</div>
<?php endforeach; ?>
</div>
<?php endif; ?>
</div>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
// Mark individual notification as read
document.querySelectorAll('.mark-read-btn').forEach(button => {
button.addEventListener('click', async function() {
const notificationId = this.closest('.list-group-item').dataset.notificationId;
try {
const response = await fetch('mark_read.php', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
notification_ids: [notificationId]
})
});
const result = await response.json();
if (result.success) {
this.closest('.list-group-item').classList.remove('list-group-item-primary');
this.remove();
// Update unread count
const unreadCount = document.querySelectorAll('.list-group-item-primary').length;
if (unreadCount === 0) {
document.getElementById('markAllReadBtn')?.remove();
}
} else {
alert(result.error || 'Error marking notification as read. Please try again.');
}
} catch (error) {
console.error('Error:', error);
alert('Error marking notification as read. Please try again.');
}
});
});
// Mark all notifications as read
const markAllReadBtn = document.getElementById('markAllReadBtn');
if (markAllReadBtn) {
markAllReadBtn.addEventListener('click', async function() {
try {
const response = await fetch('mark_all_read.php', {
method: 'POST'
});
const result = await response.json();
if (result.success) {
document.querySelectorAll('.list-group-item-primary').forEach(item => {
item.classList.remove('list-group-item-primary');
});
document.querySelectorAll('.mark-read-btn').forEach(btn => btn.remove());
this.remove();
} else {
alert(result.error || 'Error marking notifications as read. Please try again.');
}
} catch (error) {
console.error('Error:', error);
alert('Error marking notifications as read. Please try again.');
}
});
}
});
</script>
<?php include_once '../../../f_core/footer.php'; ?>