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_customers();
|
||||||
seed_plans();
|
seed_plans();
|
||||||
|
seed_invoices();
|
||||||
|
seed_reports();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
63
invoices.php
63
invoices.php
@ -14,10 +14,65 @@ require_once 'templates/header.php';
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="text-center py-5">
|
<?php
|
||||||
<i class="bi bi-journal-text" style="font-size: 4rem; color: #ccc;"></i>
|
require_once 'db/config.php';
|
||||||
<h2 class="mt-4">Coming Soon</h2>
|
|
||||||
<p class="text-muted">The invoices management page is under construction.</p>
|
$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>
|
</div>
|
||||||
|
|
||||||
<?php
|
<?php
|
||||||
|
|||||||
139
reports.php
139
reports.php
@ -8,10 +8,141 @@ require_once 'templates/header.php';
|
|||||||
<h1 class="h2">Reports</h1>
|
<h1 class="h2">Reports</h1>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="text-center py-5">
|
<?php
|
||||||
<i class="bi bi-graph-up" style="font-size: 4rem; color: #ccc;"></i>
|
require_once 'db/config.php';
|
||||||
<h2 class="mt-4">Coming Soon</h2>
|
|
||||||
<p class="text-muted">The reports and analytics page is under construction.</p>
|
$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>
|
</div>
|
||||||
|
|
||||||
<?php
|
<?php
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user