diff --git a/assets/js/billing.js b/assets/js/billing.js
new file mode 100644
index 0000000..282d32e
--- /dev/null
+++ b/assets/js/billing.js
@@ -0,0 +1,30 @@
+function initBillingPage(projectId) {
+ document.querySelectorAll('.billing-amount').forEach(input => {
+ input.addEventListener('blur', function() {
+ const month = this.dataset.month;
+ const amount = this.value;
+
+ const formData = new FormData();
+ formData.append('projectId', projectId);
+ formData.append('month', month);
+ formData.append('amount', amount);
+
+ fetch('save_billing.php', {
+ method: 'POST',
+ body: formData
+ })
+ .then(response => response.json())
+ .then(data => {
+ if (data.success) {
+ // Maybe show a small success indicator
+ } else {
+ alert('Error saving billing amount: ' + data.error);
+ }
+ })
+ .catch(error => {
+ console.error('Error:', error);
+ alert('An unexpected error occurred.');
+ });
+ });
+ });
+}
diff --git a/billing.php b/billing.php
new file mode 100644
index 0000000..e404f20
--- /dev/null
+++ b/billing.php
@@ -0,0 +1,114 @@
+prepare("SELECT * FROM projects WHERE id = :id");
+ $stmt->execute([':id' => $projectId]);
+ $project = $stmt->fetch(PDO::FETCH_ASSOC);
+
+ if (!$project) {
+ header("Location: projects.php");
+ exit();
+ }
+
+ // Initialize billing records if they don't exist
+ $startDate = new DateTime($project['startDate']);
+ $endDate = new DateTime($project['endDate']);
+ $currentMonth = clone $startDate;
+
+ while ($currentMonth <= $endDate) {
+ $monthStr = $currentMonth->format('Y-m-01');
+ $stmt = $pdo->prepare("SELECT COUNT(*) FROM billingMonthly WHERE projectId = :projectId AND month = :month");
+ $stmt->execute([':projectId' => $projectId, ':month' => $monthStr]);
+ $count = $stmt->fetchColumn();
+
+ if ($count == 0) {
+ $insertStmt = $pdo->prepare("INSERT INTO billingMonthly (projectId, month, amount) VALUES (:projectId, :month, 0)");
+ $insertStmt->execute([':projectId' => $projectId, ':month' => $monthStr]);
+ }
+
+ $currentMonth->modify('+1 month');
+ }
+
+ // Fetch billing data
+ $stmt = $pdo->prepare("SELECT * FROM billingMonthly WHERE projectId = :projectId ORDER BY month");
+ $stmt->execute([':projectId' => $projectId]);
+ $billingData = $stmt->fetchAll(PDO::FETCH_ASSOC);
+
+} catch (PDOException $e) {
+ $db_error = "Database error: " . $e->getMessage();
+}
+
+?>
+
+
+
+
+
+ Billing -
+
+
+
+
+
+
+
+
+
+
+
diff --git a/db/migrations/006_create_billing_monthly_table.sql b/db/migrations/006_create_billing_monthly_table.sql
new file mode 100644
index 0000000..1f8748a
--- /dev/null
+++ b/db/migrations/006_create_billing_monthly_table.sql
@@ -0,0 +1,8 @@
+CREATE TABLE IF NOT EXISTS `billingMonthly` (
+ `id` INT AUTO_INCREMENT PRIMARY KEY,
+ `projectId` INT NOT NULL,
+ `month` DATE NOT NULL,
+ `amount` DECIMAL(10, 2) NOT NULL DEFAULT 0.00,
+ FOREIGN KEY (`projectId`) REFERENCES `projects`(`id`) ON DELETE CASCADE,
+ UNIQUE KEY `project_month` (`projectId`, `month`)
+);
diff --git a/export_billing.php b/export_billing.php
new file mode 100644
index 0000000..733e481
--- /dev/null
+++ b/export_billing.php
@@ -0,0 +1,46 @@
+prepare("SELECT name FROM projects WHERE id = :id");
+ $stmt->execute([':id' => $projectId]);
+ $project = $stmt->fetch(PDO::FETCH_ASSOC);
+ $projectName = $project ? $project['name'] : 'project';
+
+ $stmt = $pdo->prepare("SELECT month, amount FROM billingMonthly WHERE projectId = :projectId ORDER BY month");
+ $stmt->execute([':projectId' => $projectId]);
+ $billingData = $stmt->fetchAll(PDO::FETCH_ASSOC);
+
+ $filename = "billing_" . strtolower(str_replace(' ', '_', $projectName)) . ".csv";
+
+ header('Content-Type: text/csv');
+ header('Content-Disposition: attachment; filename="' . $filename . '"');
+
+ $output = fopen('php://output', 'w');
+
+ // Header row
+ $header = ['Billing'];
+ foreach ($billingData as $row) {
+ $header[] = date("M Y", strtotime($row['month']));
+ }
+ fputcsv($output, $header);
+
+ // Data row
+ $data = ['Billing'];
+ foreach ($billingData as $row) {
+ $data[] = $row['amount'];
+ }
+ fputcsv($output, $data);
+
+ fclose($output);
+ exit();
+
+} catch (PDOException $e) {
+ die("Database error: " . $e->getMessage());
+}
diff --git a/project_details.php b/project_details.php
index a09fab4..a64c2b4 100644
--- a/project_details.php
+++ b/project_details.php
@@ -137,6 +137,7 @@ if (!$project) {
diff --git a/save_billing.php b/save_billing.php
new file mode 100644
index 0000000..4481695
--- /dev/null
+++ b/save_billing.php
@@ -0,0 +1,36 @@
+ false, 'error' => 'Invalid request'];
+
+if ($_SERVER['REQUEST_METHOD'] === 'POST') {
+ $projectId = $_POST['projectId'] ?? null;
+ $month = $_POST['month'] ?? null;
+ $amount = $_POST['amount'] ?? null;
+
+ if ($projectId && $month && $amount !== null) {
+ try {
+ $pdo = db();
+ $stmt = $pdo->prepare("UPDATE billingMonthly SET amount = :amount WHERE projectId = :projectId AND month = :month");
+ $stmt->execute([
+ ':amount' => $amount,
+ ':projectId' => $projectId,
+ ':month' => $month
+ ]);
+
+ if ($stmt->rowCount() > 0) {
+ $response['success'] = true;
+ unset($response['error']);
+ } else {
+ $response['error'] = 'No record found to update.';
+ }
+ } catch (PDOException $e) {
+ $response['error'] = 'Database error: ' . $e->getMessage();
+ }
+ } else {
+ $response['error'] = 'Missing required fields.';
+ }
+}
+
+echo json_encode($response);