Auto commit: 2025-10-26T16:46:07.096Z
This commit is contained in:
parent
4f6f10c21f
commit
8daee549e1
9
db/migrations/003_create_invoices_table.sql
Normal file
9
db/migrations/003_create_invoices_table.sql
Normal file
@ -0,0 +1,9 @@
|
||||
CREATE TABLE IF NOT EXISTS invoices (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
customer_id INT NOT NULL,
|
||||
amount DECIMAL(10, 2) NOT NULL,
|
||||
status VARCHAR(20) NOT NULL DEFAULT 'pending',
|
||||
due_date DATE,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (customer_id) REFERENCES customers(id) ON DELETE CASCADE
|
||||
);
|
||||
8
db/migrations/004_create_reports_table.sql
Normal file
8
db/migrations/004_create_reports_table.sql
Normal file
@ -0,0 +1,8 @@
|
||||
|
||||
CREATE TABLE IF NOT EXISTS reports (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
type VARCHAR(100) NOT NULL,
|
||||
data TEXT,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
73
db/seed.php
73
db/seed.php
@ -59,7 +59,80 @@ function seed_plans() {
|
||||
}
|
||||
}
|
||||
|
||||
function seed_invoices() {
|
||||
$pdo = db();
|
||||
$customer_stmt = $pdo->query('SELECT id FROM customers');
|
||||
$customer_ids = $customer_stmt->fetchAll(PDO::FETCH_COLUMN);
|
||||
|
||||
if (empty($customer_ids)) {
|
||||
echo "No customers found to create invoices for.\n";
|
||||
return;
|
||||
}
|
||||
|
||||
$invoices = [];
|
||||
$statuses = ['Paid', 'Pending', 'Overdue'];
|
||||
foreach ($customer_ids as $customer_id) {
|
||||
for ($i = 0; $i < 3; $i++) {
|
||||
$invoices[] = [
|
||||
'customer_id' => $customer_id,
|
||||
'amount' => rand(2000, 10000) / 100,
|
||||
'status' => $statuses[array_rand($statuses)],
|
||||
'due_date' => date('Y-m-d', strtotime('+' . rand(-30, 30) . ' days')),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
$stmt = $pdo->prepare("INSERT INTO invoices (customer_id, amount, status, due_date) VALUES (:customer_id, :amount, :status, :due_date)");
|
||||
|
||||
foreach ($invoices as $invoice) {
|
||||
echo "Seeding invoice for customer ID: " . $invoice['customer_id'] . "...\n";
|
||||
try {
|
||||
$stmt->execute($invoice);
|
||||
echo "Success.\n";
|
||||
} catch (PDOException $e) {
|
||||
echo "Error seeding invoice: " . $e->getMessage() . "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function seed_reports() {
|
||||
$pdo = db();
|
||||
$reports = [
|
||||
[
|
||||
'name' => 'Monthly Revenue',
|
||||
'type' => 'Financial',
|
||||
'data' => json_encode(['month' => 'October 2025', 'revenue' => 12345.67])
|
||||
],
|
||||
[
|
||||
'name' => 'Active Customers',
|
||||
'type' => 'User',
|
||||
'data' => json_encode(['active_customers' => 123])
|
||||
],
|
||||
[
|
||||
'name' => 'Plan Distribution',
|
||||
'type' => 'Subscription',
|
||||
'data' => json_encode(['Basic' => 50, 'Premium HD' => 50, 'Family Pack' => 23])
|
||||
]
|
||||
];
|
||||
|
||||
$stmt = $pdo->prepare("INSERT INTO reports (name, type, data) VALUES (:name, :type, :data)");
|
||||
|
||||
foreach ($reports as $report) {
|
||||
echo "Seeding report: " . $report['name'] . "...\n";
|
||||
try {
|
||||
$stmt->execute($report);
|
||||
echo "Success.\n";
|
||||
} catch (PDOException $e) {
|
||||
echo "Error seeding report " . $report['name'] . ": " . $e->getMessage() . "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
seed_customers();
|
||||
seed_plans();
|
||||
seed_invoices();
|
||||
seed_reports();
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
63
invoices.php
63
invoices.php
@ -14,10 +14,65 @@ require_once 'templates/header.php';
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="text-center py-5">
|
||||
<i class="bi bi-journal-text" style="font-size: 4rem; color: #ccc;"></i>
|
||||
<h2 class="mt-4">Coming Soon</h2>
|
||||
<p class="text-muted">The invoices management page is under construction.</p>
|
||||
<?php
|
||||
require_once 'db/config.php';
|
||||
|
||||
$pdo = db();
|
||||
$stmt = $pdo->query('SELECT invoices.*, customers.name AS customer_name FROM invoices LEFT JOIN customers ON invoices.customer_id = customers.id ORDER BY invoices.created_at DESC');
|
||||
$invoices = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
$status_colors = [
|
||||
'Paid' => 'success',
|
||||
'Pending' => 'warning',
|
||||
'Overdue' => 'danger',
|
||||
];
|
||||
|
||||
?>
|
||||
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped table-sm">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Invoice #</th>
|
||||
<th>Customer</th>
|
||||
<th>Amount</th>
|
||||
<th>Status</th>
|
||||
<th>Due Date</th>
|
||||
<th>Created</th>
|
||||
<th>Action</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php if (empty($invoices)): ?>
|
||||
<tr>
|
||||
<td colspan="7" class="text-center py-5">
|
||||
<i class="bi bi-journal-text" style="font-size: 3rem; color: #ccc;"></i>
|
||||
<h2 class="mt-3">No Invoices Found</h2>
|
||||
<p class="text-muted">There are no invoices to display yet.</p>
|
||||
</td>
|
||||
</tr>
|
||||
<?php else: ?>
|
||||
<?php foreach ($invoices as $invoice): ?>
|
||||
<tr>
|
||||
<td>#<?php echo htmlspecialchars($invoice['id']); ?></td>
|
||||
<td><?php echo htmlspecialchars($invoice['customer_name']); ?></td>
|
||||
<td>$<?php echo number_format($invoice['amount'], 2); ?></td>
|
||||
<td>
|
||||
<span class="badge bg-<?php echo $status_colors[$invoice['status']] ?? 'secondary'; ?>">
|
||||
<?php echo htmlspecialchars($invoice['status']); ?>
|
||||
</span>
|
||||
</td>
|
||||
<td><?php echo date('M d, Y', strtotime($invoice['due_date'])); ?></td>
|
||||
<td><?php echo date('M d, Y', strtotime($invoice['created_at'])); ?></td>
|
||||
<td>
|
||||
<button class="btn btn-sm btn-outline-secondary"><i class="bi bi-eye"></i></button>
|
||||
<button class="btn btn-sm btn-outline-secondary"><i class="bi bi-pencil"></i></button>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
<?php endif; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<?php
|
||||
|
||||
139
reports.php
139
reports.php
@ -8,10 +8,141 @@ require_once 'templates/header.php';
|
||||
<h1 class="h2">Reports</h1>
|
||||
</div>
|
||||
|
||||
<div class="text-center py-5">
|
||||
<i class="bi bi-graph-up" style="font-size: 4rem; color: #ccc;"></i>
|
||||
<h2 class="mt-4">Coming Soon</h2>
|
||||
<p class="text-muted">The reports and analytics page is under construction.</p>
|
||||
<?php
|
||||
require_once 'db/config.php';
|
||||
|
||||
$pdo = db();
|
||||
|
||||
// Fetch stats
|
||||
$total_customers = $pdo->query('SELECT COUNT(*) FROM customers')->fetchColumn();
|
||||
$active_customers = $pdo->query("SELECT COUNT(*) FROM customers WHERE status = 'Active'")->fetchColumn();
|
||||
$total_plans = $pdo->query('SELECT COUNT(*) FROM plans')->fetchColumn();
|
||||
$total_revenue = $pdo->query("SELECT SUM(amount) FROM invoices WHERE status = 'Paid'")->fetchColumn();
|
||||
|
||||
$invoice_status_stmt = $pdo->query("SELECT status, COUNT(*) as count FROM invoices GROUP BY status");
|
||||
$invoice_statuses = $invoice_status_stmt->fetchAll(PDO::FETCH_KEY_PAIR);
|
||||
|
||||
$total_invoices = array_sum($invoice_statuses);
|
||||
$paid_invoices = $invoice_statuses['Paid'] ?? 0;
|
||||
$pending_invoices = $invoice_statuses['Pending'] ?? 0;
|
||||
$overdue_invoices = $invoice_statuses['Overdue'] ?? 0;
|
||||
|
||||
?>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-3 mb-4">
|
||||
<div class="card text-white bg-primary">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title"><i class="bi bi-people-fill"></i> Total Customers</h5>
|
||||
<p class="card-text fs-4"><?php echo $total_customers; ?></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3 mb-4">
|
||||
<div class="card text-white bg-success">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title"><i class="bi bi-person-check-fill"></i> Active Customers</h5>
|
||||
<p class="card-text fs-4"><?php echo $active_customers; ?></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3 mb-4">
|
||||
<div class="card text-white bg-info">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title"><i class="bi bi-tags-fill"></i> Available Plans</h5>
|
||||
<p class="card-text fs-4"><?php echo $total_plans; ?></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3 mb-4">
|
||||
<div class="card text-white bg-dark">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title"><i class="bi bi-cash-stack"></i> Total Revenue</h5>
|
||||
<p class="card-text fs-4">$<?php echo number_format($total_revenue, 2); ?></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row mt-4">
|
||||
<div class="col-md-12">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<i class="bi bi-journal-text"></i> Invoice Status Distribution
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="progress" style="height: 30px;">
|
||||
<div class="progress-bar bg-success" role="progressbar" style="width: <?php echo ($paid_invoices / $total_invoices) * 100; ?>%" aria-valuenow="<?php echo $paid_invoices; ?>" aria-valuemin="0" aria-valuemax="<?php echo $total_invoices; ?>">Paid</div>
|
||||
<div class="progress-bar bg-warning" role="progressbar" style="width: <?php echo ($pending_invoices / $total_invoices) * 100; ?>%" aria-valuenow="<?php echo $pending_invoices; ?>" aria-valuemin="0" aria-valuemax="<?php echo $total_invoices; ?>">Pending</div>
|
||||
<div class="progress-bar bg-danger" role="progressbar" style="width: <?php echo ($overdue_invoices / $total_invoices) * 100; ?>%" aria-valuenow="<?php echo $overdue_invoices; ?>" aria-valuemin="0" aria-valuemax="<?php echo $total_invoices; ?>">Overdue</div>
|
||||
</div>
|
||||
<div class="d-flex justify-content-around mt-3 text-center">
|
||||
<div>
|
||||
<p class="mb-0 fs-5"><?php echo $paid_invoices; ?></p>
|
||||
<span class="text-success">Paid</span>
|
||||
</div>
|
||||
<div>
|
||||
<p class="mb-0 fs-5"><?php echo $pending_invoices; ?></p>
|
||||
<span class="text-warning">Pending</span>
|
||||
</div>
|
||||
<div>
|
||||
<p class="mb-0 fs-5"><?php echo $overdue_invoices; ?></p>
|
||||
<span class="text-danger">Overdue</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row mt-4">
|
||||
<div class="col-md-12">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<i class="bi bi-journal-text"></i> Generated Reports
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped table-sm">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Report Name</th>
|
||||
<th>Type</th>
|
||||
<th>Date Generated</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php
|
||||
$stmt = $pdo->query('SELECT * FROM reports ORDER BY created_at DESC');
|
||||
$reports = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
if (empty($reports)):
|
||||
?>
|
||||
<tr>
|
||||
<td colspan="4" class="text-center py-5">
|
||||
<i class="bi bi-journal-x" style="font-size: 3rem; color: #ccc;"></i>
|
||||
<h2 class="mt-3">No Reports Found</h2>
|
||||
<p class="text-muted">There are no generated reports to display yet.</p>
|
||||
</td>
|
||||
</tr>
|
||||
<?php else: ?>
|
||||
<?php foreach ($reports as $report): ?>
|
||||
<tr>
|
||||
<td><?php echo htmlspecialchars($report['name']); ?></td>
|
||||
<td><span class="badge bg-secondary"><?php echo htmlspecialchars($report['type']); ?></span></td>
|
||||
<td><?php echo date('M d, Y H:i A', strtotime($report['created_at'])); ?></td>
|
||||
<td>
|
||||
<button class="btn btn-sm btn-outline-primary"><i class="bi bi-eye"></i> View</button>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
<?php endif; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user