diff --git a/assets/css/custom.css b/assets/css/custom.css
new file mode 100644
index 0000000..4b44c57
--- /dev/null
+++ b/assets/css/custom.css
@@ -0,0 +1,14 @@
+body {
+ font-family: 'Inter', sans-serif;
+ background-color: #F8F9FA;
+}
+
+.gradient-header {
+ background: linear-gradient(to right, #0D6EFD, #0A58CA);
+ color: white;
+}
+
+.card {
+ border-radius: 0.375rem;
+ box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075);
+}
diff --git a/assets/js/main.js b/assets/js/main.js
new file mode 100644
index 0000000..33eb62a
--- /dev/null
+++ b/assets/js/main.js
@@ -0,0 +1,7 @@
+document.addEventListener('DOMContentLoaded', function () {
+ const toastElList = [].slice.call(document.querySelectorAll('.toast'));
+ const toastList = toastElList.map(function (toastEl) {
+ return new bootstrap.Toast(toastEl, { delay: 3000 });
+ });
+ toastList.forEach(toast => toast.show());
+});
diff --git a/db/config.php b/db/config.php
index cc9229f..3b67cac 100644
--- a/db/config.php
+++ b/db/config.php
@@ -1,17 +1,70 @@
PDO::ERRMODE_EXCEPTION,
- PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
- ]);
- }
- return $pdo;
+ $host = getenv('DB_HOST') ?: '127.0.0.1';
+ $port = getenv('DB_PORT') ?: '3306';
+ $db = getenv('DB_DATABASE') ?: 'flatlogic';
+ $user = getenv('DB_USERNAME') ?: 'flatlogic';
+ $pass = getenv('DB_PASSWORD') ?: 'flatlogic';
+ $charset = 'utf8mb4';
+
+ $dsn = "mysql:host=$host;port=$port;dbname=$db;charset=$charset";
+ $options = [
+ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
+ PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
+ PDO::ATTR_EMULATE_PREPARES => false,
+ ];
+
+ try {
+ $pdo = new PDO($dsn, $user, $pass, $options);
+ run_migrations($pdo);
+ return $pdo;
+ } catch (\PDOException $e) {
+ error_log($e->getMessage());
+ return null;
+ }
}
+
+function run_migrations(PDO $pdo): void {
+ $migrations_dir = __DIR__ . '/migrations';
+ if (!is_dir($migrations_dir)) {
+ return;
+ }
+
+ try {
+ $pdot = $pdo->query("SHOW TABLES LIKE 'migrations'");
+ $table_exists = $pdot->rowCount() > 0;
+
+ if (!$table_exists) {
+ $pdo->exec("CREATE TABLE migrations (
+ id INT AUTO_INCREMENT PRIMARY KEY,
+ migration VARCHAR(255) NOT NULL,
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
+ ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;");
+ }
+
+ $stmt = $pdo->prepare("SELECT migration FROM migrations");
+ $stmt->execute();
+ $run_migrations = $stmt->fetchAll(PDO::FETCH_COLUMN);
+
+ $migration_files = glob($migrations_dir . '/*.sql');
+ foreach ($migration_files as $file) {
+ $migration_name = basename($file);
+ if (!in_array($migration_name, $run_migrations)) {
+ $sql = file_get_contents($file);
+ if ($sql === false) continue;
+ $pdo->exec($sql);
+
+ $stmt = $pdo->prepare("INSERT INTO migrations (migration) VALUES (?)");
+ $stmt->execute([$migration_name]);
+ }
+ }
+ } catch (\PDOException $e) {
+ error_log("Migration failed: " . $e->getMessage());
+ }
+}
\ No newline at end of file
diff --git a/db/migrations/001_create_initial_tables.sql b/db/migrations/001_create_initial_tables.sql
new file mode 100644
index 0000000..07b1e3d
--- /dev/null
+++ b/db/migrations/001_create_initial_tables.sql
@@ -0,0 +1,17 @@
+CREATE TABLE IF NOT EXISTS categories (
+ id INT AUTO_INCREMENT PRIMARY KEY,
+ name VARCHAR(255) NOT NULL UNIQUE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
+
+INSERT IGNORE INTO categories (name) VALUES ('Food'), ('Pharmacy'), ('Transport'), ('Shopping');
+
+CREATE TABLE IF NOT EXISTS expenses (
+ id INT AUTO_INCREMENT PRIMARY KEY,
+ amount DECIMAL(10, 2) NOT NULL,
+ category_id INT NOT NULL,
+ payment_method VARCHAR(255) NOT NULL,
+ notes TEXT,
+ expense_date DATE NOT NULL,
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ FOREIGN KEY (category_id) REFERENCES categories(id)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
diff --git a/index.php b/index.php
index 7205f3d..ca233bb 100644
--- a/index.php
+++ b/index.php
@@ -1,150 +1,225 @@
prepare("DELETE FROM expenses WHERE id = ?");
+ $stmt->execute([$id_to_delete]);
+ $_SESSION['message'] = 'Expense deleted successfully.';
+ $_SESSION['message_type'] = 'success';
+ } catch (PDOException $e) {
+ $_SESSION['message'] = 'Error deleting expense: ' . $e->getMessage();
+ $_SESSION['message_type'] = 'danger';
+ }
+ }
+ header("Location: index.php");
+ exit;
+ }
+
+ // Handle Add
+ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['add_expense'])) {
+ $amount = filter_input(INPUT_POST, 'amount', FILTER_VALIDATE_FLOAT);
+ $category_id = filter_input(INPUT_POST, 'category_id', FILTER_VALIDATE_INT);
+ $payment_method = htmlspecialchars(trim($_POST['payment_method']));
+ $expense_date = $_POST['expense_date']; // Basic validation, consider more robust date validation
+ $notes = htmlspecialchars(trim($_POST['notes']));
+
+ if ($amount && $category_id && !empty($payment_method) && !empty($expense_date)) {
+ try {
+ $stmt = $pdo->prepare("INSERT INTO expenses (amount, category_id, payment_method, expense_date, notes) VALUES (?, ?, ?, ?, ?)");
+ $stmt->execute([$amount, $category_id, $payment_method, $expense_date, $notes]);
+ $_SESSION['message'] = 'Expense added successfully!';
+ $_SESSION['message_type'] = 'success';
+ } catch (PDOException $e) {
+ $_SESSION['message'] = 'Error adding expense: ' . $e->getMessage();
+ $_SESSION['message_type'] = 'danger';
+ }
+ } else {
+ $_SESSION['message'] = 'Please fill all required fields correctly.';
+ $_SESSION['message_type'] = 'warning';
+ }
+ header("Location: index.php");
+ exit;
+ }
+}
+
+
+// Flash message handling
+if (isset($_SESSION['message'])) {
+ $message = $_SESSION['message'];
+ $message_type = $_SESSION['message_type'];
+ unset($_SESSION['message']);
+ unset($_SESSION['message_type']);
+}
+
+// Fetch data for display
+$categories = [];
+$expenses = [];
+if ($pdo) {
+ try {
+ $categories = $pdo->query("SELECT * FROM categories ORDER BY name ASC")->fetchAll();
+ $expenses = $pdo->query("SELECT e.*, c.name as category_name FROM expenses e JOIN categories c ON e.category_id = c.id ORDER BY e.expense_date DESC, e.id DESC LIMIT 20")->fetchAll();
+ } catch (PDOException $e) {
+ $message = "Error fetching data: " . $e->getMessage();
+ $message_type = 'danger';
+ }
+}
-$phpVersion = PHP_VERSION;
-$now = date('Y-m-d H:i:s');
?>
-
+
-
-
- New Style
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+ Personal Expense Tracker
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
Analyzing your requirements and generating your website…
-
- Loading…
-
-
= ($_SERVER['HTTP_HOST'] ?? '') === 'appwizzy.com' ? 'AppWizzy' : 'Flatlogic' ?> AI is collecting your requirements and applying the first changes.
-
This page will update automatically as the plan is implemented.
-
Runtime: PHP = htmlspecialchars($phpVersion) ?> — UTC = htmlspecialchars($now) ?>
+
+
+
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ | Date |
+ Amount |
+ Category |
+ Payment Method |
+ Notes |
+ Action |
+
+
+
+
+
+ | No expenses recorded yet. |
+
+
+
+
+ |
+ $ |
+ |
+ |
+ |
+
+
+
+
+ |
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+