208 lines
11 KiB
PHP
208 lines
11 KiB
PHP
<?php
|
|
require_once 'auth.php';
|
|
require_login();
|
|
|
|
require_once 'db/config.php';
|
|
|
|
// Simple Migration Runner
|
|
try {
|
|
$pdo = db();
|
|
$pdo->exec("CREATE TABLE IF NOT EXISTS migrations (migration VARCHAR(255) PRIMARY KEY, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP)");
|
|
|
|
$ran_migrations_stmt = $pdo->query("SELECT migration FROM migrations");
|
|
$ran_migrations = $ran_migrations_stmt->fetchAll(PDO::FETCH_COLUMN);
|
|
|
|
$migration_files = glob('db/migrations/*.sql');
|
|
sort($migration_files);
|
|
|
|
foreach ($migration_files as $file) {
|
|
$migration_name = basename($file);
|
|
if (!in_array($migration_name, $ran_migrations)) {
|
|
$sql = file_get_contents($file);
|
|
$pdo->exec($sql);
|
|
$stmt = $pdo->prepare("INSERT INTO migrations (migration) VALUES (?)");
|
|
$stmt->execute([$migration_name]);
|
|
}
|
|
}
|
|
} catch (Exception $e) {
|
|
// In a real app, you'd want to log this error and show a user-friendly message.
|
|
die("Database migration failed: " . $e->getMessage());
|
|
}
|
|
?>
|
|
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>My Deals - LTD Tracker</title>
|
|
<meta name="description" content="Lifetime SaaS Tracker - Organize and monitor your software deals effortlessly with user ratings and easy access.">
|
|
|
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
|
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css">
|
|
<link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/1.13.6/css/dataTables.bootstrap5.min.css">
|
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-multiselect/1.1.2/css/bootstrap-multiselect.css">
|
|
<link rel="stylesheet" href="assets/css/custom.css?v=<?php echo time(); ?>">
|
|
</head>
|
|
<body>
|
|
|
|
<nav class="navbar navbar-expand-lg sticky-top">
|
|
<div class="container">
|
|
<a class="navbar-brand" href="#">LTD Tracker</a>
|
|
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
|
|
<span class="navbar-toggler-icon"></span>
|
|
</button>
|
|
<div class="collapse navbar-collapse" id="navbarNav">
|
|
<ul class="navbar-nav ms-auto">
|
|
<li class="nav-item"><button id="add-deal-btn" class="btn btn-primary me-3">Add New Deal</button></li>
|
|
<li class="nav-item"><span class="nav-link">Role: <?php echo htmlspecialchars($_SESSION['role_name']); ?></span></li>
|
|
<?php if (is_admin()): ?>
|
|
<li class="nav-item"><a href="admin.php" class="nav-link">Admin</a></li>
|
|
<?php endif; ?>
|
|
<li class="nav-item"><a href="profile.php" class="nav-link">Profile</a></li>
|
|
<li class="nav-item"><a href="logout.php" class="btn btn-outline-secondary ms-2">Logout</a></li>
|
|
<li class="nav-item d-flex align-items-center ms-3">
|
|
<div class="theme-switcher" id="theme-switcher">
|
|
<i id="theme-icon" data-feather="moon"></i>
|
|
</div>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
</nav>
|
|
|
|
<main class="container mt-5">
|
|
<section id="dashboard" class="section pt-5">
|
|
<div class="row">
|
|
<div class="col-md-3">
|
|
<div class="card text-center">
|
|
<div class="card-body">
|
|
<h5 class="card-title">Total Deals</h5>
|
|
<p class="card-text fs-4" id="total-deals-stat">0</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-3">
|
|
<div class="card text-center">
|
|
<div class="card-body">
|
|
<h5 class="card-title">Total Spent</h5>
|
|
<p class="card-text fs-4" id="total-spent-stat">$0.00</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-3">
|
|
<div class="card text-center">
|
|
<div class="card-body">
|
|
<h5 class="card-title">Average Rating</h5>
|
|
<p class="card-text fs-4" id="avg-rating-stat">0.0</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-3">
|
|
<div class="card text-center">
|
|
<div class="card-body">
|
|
<h5 class="card-title">New Deals (30d)</h5>
|
|
<p class="card-text fs-4" id="deals-last-30-days-stat">0</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="row mt-4">
|
|
<div class="col-md-6">
|
|
<div class="card">
|
|
<div class="card-body">
|
|
<h5 class="card-title">Deals by Category</h5>
|
|
<canvas id="category-chart"></canvas>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<section id="deals" class="section">
|
|
<h1 class="text-center mb-5">My Deals</h1>
|
|
<div class="row mb-4">
|
|
<div class="col-md-4">
|
|
<select id="category-filter" class="form-select" aria-label="Filter by category">
|
|
<option value="">All Categories</option>
|
|
</select>
|
|
</div>
|
|
<div class="col-md-8">
|
|
<select id="tags-filter" class="form-select" multiple aria-label="Filter by tags">
|
|
<!-- Tags will be populated by JS -->
|
|
</select>
|
|
</div>
|
|
</div>
|
|
<div class="table-responsive">
|
|
<table id="deals-table" class="table table-striped table-bordered" style="width:100%">
|
|
<thead>
|
|
<tr>
|
|
<th>Name</th>
|
|
<th>Vendor</th>
|
|
<th>Category</th>
|
|
<th>Purchase Date</th>
|
|
<th>Price</th>
|
|
<th>Rating</th>
|
|
<th>Website</th>
|
|
<th>Actions</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<!-- Data will be injected by JS -->
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</section>
|
|
</main>
|
|
|
|
<footer class="py-4 bg-dark text-white text-center mt-5">
|
|
<div class="container">
|
|
<p class="mb-0">© <?php echo date('Y'); ?> LTD Tracker. All rights reserved.</p>
|
|
<a href="/privacy.php" class="text-white">Privacy Policy</a>
|
|
</div>
|
|
</footer>
|
|
|
|
<!-- Deal Modal -->
|
|
<div class="modal fade" id="dealModal" tabindex="-1" aria-labelledby="dealModalLabel" aria-hidden="true">
|
|
<div class="modal-dialog modal-lg">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title" id="dealModalLabel">Add New Deal</h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<form id="dealForm" enctype="multipart/form-data">
|
|
<div class="row">
|
|
<div class="col-md-6 mb-3"><input type="text" class="form-control" name="name" placeholder="Deal Name" required></div>
|
|
<div class="col-md-6 mb-3"><input type="text" class="form-control" name="vendor" placeholder="Vendor"></div>
|
|
<div class="col-md-12 mb-3"><input type="url" class="form-control" name="website" placeholder="Website URL"></div>
|
|
<div class="col-md-6 mb-3"><label class="form-label">Purchase Date</label><input type="date" class="form-control" name="purchaseDate"></div>
|
|
<div class="col-md-3 mb-3"><label class="form-label">Price</label><input type="number" step="0.01" class="form-control" name="price" placeholder="0.00"></div>
|
|
<div class="col-md-3 mb-3"><label class="form-label">Currency</label><input type="text" class="form-control" name="currency" value="USD"></div>
|
|
<div class="col-md-6 mb-3"><input type="text" class="form-control" name="category" placeholder="Category (e.g., Marketing)"></div>
|
|
<div class="col-md-6 mb-3"><input type="text" class="form-control" name="tags" placeholder="Tags (comma-separated)"></div>
|
|
<div class="col-md-6 mb-3"><input type="text" class="form-control" name="username" placeholder="Username"></div>
|
|
<div class="col-md-6 mb-3"><input type="password" class="form-control" name="password" placeholder="Password"></div>
|
|
<div class="col-md-12 mb-3" id="deal-files-list"></div>
|
|
<div class="col-md-12 mb-3"><label for="deal-files" class="form-label">Attach Files (e.g., receipts, licenses)</label><input type="file" class="form-control" name="files[]" id="deal-files" multiple></div>
|
|
<div class="col-md-12 mb-3"><label class="form-label">Rating (1-5)</label><input type="range" class="form-range" name="rating" min="1" max="5" step="1"></div>
|
|
</div>
|
|
<button type="submit" class="btn btn-primary">Save Deal</button>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script src="https://code.jquery.com/jquery-3.7.0.js"></script>
|
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
|
|
<script src="https://cdn.datatables.net/1.13.6/js/jquery.dataTables.min.js"></script>
|
|
<script src="https://cdn.datatables.net/1.13.6/js/dataTables.bootstrap5.min.js"></script>
|
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-multiselect/1.1.2/js/bootstrap-multiselect.js"></script>
|
|
<script src="https://cdn.jsdelivr.net/npm/feather-icons/dist/feather.min.js"></script>
|
|
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
|
<script src="assets/js/main.js?v=<?php echo time(); ?>"></script>
|
|
<script>
|
|
feather.replace();
|
|
</script>
|
|
</body>
|
|
</html>
|