Add files via upload

This commit is contained in:
ssbg04 authored and GitHub committed 2025-06-23 21:41:03 +08:00
1 parent cf2a17298d
commit 54c527b036
65 files changed
+13328

No files matched your search

+20
View File
@@ -0,0 +1,20 @@
<li class="nav-item">
<a class="nav-link" href="<?php echo BASE_URL; ?>owner/products/list.php">
<i class="fas fa-box me-2"></i>Products
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="<?php echo BASE_URL; ?>owner/orders/list.php">
<i class="fas fa-shopping-cart me-2"></i>Orders
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="<?php echo BASE_URL; ?>owner/reports.php">
<i class="fas fa-chart-bar me-2"></i>Reports
</a>
</li>
</ul>
</div>
</div>
</div>
</div>
+531
View File
@@ -0,0 +1,531 @@
<?php
require_once '../includes/auth_functions.php';
redirectIfNotLoggedIn();
redirectIfNotAdmin();
require_once '../config/database.php';
// Get total users count
$stmt = $pdo->prepare("SELECT COUNT(*) FROM users");
$stmt->execute();
$total_users = $stmt->fetchColumn();
// Get total products count
$stmt = $pdo->prepare("SELECT COUNT(*) FROM products");
$stmt->execute();
$total_products = $stmt->fetchColumn();
// Get total orders count
$stmt = $pdo->prepare("SELECT COUNT(*) FROM orders");
$stmt->execute();
$total_orders = $stmt->fetchColumn();
// Get recent users
$stmt = $pdo->prepare("SELECT * FROM users ORDER BY user_id DESC LIMIT 5");
$stmt->execute();
$recent_users = $stmt->fetchAll();
// Get recent orders
$stmt = $pdo->prepare("
SELECT o.*, u.username as customer_name
FROM orders o
JOIN users u ON o.customer_id = u.user_id
ORDER BY o.order_date DESC
LIMIT 5
");
$stmt->execute();
$recent_orders = $stmt->fetchAll();
// Get admin's products
$stmt = $pdo->prepare("SELECT * FROM products WHERE owner_id = ? ORDER BY created_at DESC");
$stmt->execute([$_SESSION['user_id']]);
$admin_products = $stmt->fetchAll();
// Get admin information
$admin_stmt = $pdo->prepare("SELECT * FROM users WHERE user_id = ? AND role = 'admin'");
$admin_stmt->execute([$_SESSION['user_id']]);
$admin = $admin_stmt->fetch();
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Admin Dashboard - UniMarket</title>
<link rel="stylesheet" href="/unimarket/assets/css/style.css">
<link rel="stylesheet" href="/unimarket/assets/css/responsive.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/css/all.min.css">
<style>
.admin-dashboard {
padding: 20px;
}
.stats-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 20px;
margin-bottom: 30px;
}
.stat-card {
background: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
.stat-card h3 {
margin: 0 0 10px 0;
color: var(--primary-dark);
}
.stat-card .number {
font-size: 2em;
font-weight: bold;
color: var(--primary-color);
}
.recent-section {
background: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
margin-bottom: 20px;
}
.recent-section h2 {
margin: 0 0 20px 0;
color: var(--primary-dark);
}
.admin-actions {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 15px;
margin-top: 20px;
}
.admin-action-btn {
display: flex;
align-items: center;
justify-content: center;
padding: 15px;
background: var(--primary-color);
color: white;
text-decoration: none;
border-radius: 8px;
transition: background-color 0.3s;
}
.admin-action-btn:hover {
background: var(--primary-dark);
}
.admin-action-btn i {
margin-right: 10px;
}
table {
width: 100%;
border-collapse: collapse;
margin-top: 10px;
}
th, td {
padding: 12px;
text-align: left;
border-bottom: 1px solid #ddd;
}
th {
background-color: #f5f5f5;
font-weight: 600;
}
tr:hover {
background-color: #f9f9f9;
}
.btn-small {
display: inline-block;
padding: 6px 12px;
margin: 0 4px;
border-radius: 4px;
text-decoration: none;
color: white;
font-size: 0.9em;
}
.btn-edit {
background-color: #4CAF50;
color: white;
padding: 8px 16px;
border-radius: 4px;
text-decoration: none;
display: inline-flex;
align-items: center;
gap: 8px;
transition: background-color 0.3s;
}
.btn-edit:hover {
background-color: #45a049;
}
.btn-edit i {
font-size: 0.9em;
}
.btn-delete {
background-color: #dc3545;
color: white;
padding: 8px 16px;
border-radius: 4px;
text-decoration: none;
display: inline-flex;
align-items: center;
gap: 8px;
transition: all 0.3s ease;
margin-left: 8px;
}
.btn-delete:hover {
background-color: #c82333;
transform: translateY(-1px);
}
.btn-delete i {
font-size: 0.9em;
}
.edit-product {
max-width: 800px;
margin: 0 auto;
padding: 20px;
}
.form-group {
margin-bottom: 20px;
}
.form-group label {
display: block;
margin-bottom: 5px;
font-weight: 500;
}
.form-group input[type="text"],
.form-group input[type="number"],
.form-group textarea,
.form-group select {
width: 100%;
padding: 8px;
border: 1px solid #ddd;
border-radius: 4px;
font-size: 16px;
}
.form-group textarea {
height: 150px;
resize: vertical;
}
.btn-submit {
background-color: #4CAF50;
color: white;
padding: 12px 24px;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 16px;
}
.btn-submit:hover {
background-color: #45a049;
}
.alert {
padding: 15px;
margin-bottom: 20px;
border-radius: 4px;
}
.alert-success {
background-color: #dff0d8;
color: #3c763d;
border: 1px solid #d6e9c6;
}
.alert-error {
background-color: #f2dede;
color: #a94442;
border: 1px solid #ebccd1;
}
.back-button {
display: inline-flex;
align-items: center;
gap: 8px;
color: #6c757d;
text-decoration: none;
margin-bottom: 20px;
font-weight: 500;
transition: color 0.3s ease;
}
.back-button:hover {
color: #495057;
}
.back-button i {
font-size: 1.1rem;
}
.image-preview {
max-width: 200px;
margin-top: 10px;
display: none;
}
.section-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
}
.btn-add {
background-color: #4CAF50;
color: white;
padding: 8px 16px;
border-radius: 4px;
text-decoration: none;
display: inline-flex;
align-items: center;
gap: 8px;
}
.btn-add:hover {
background-color: #45a049;
}
.empty-state {
text-align: center;
padding: 40px;
background-color: #f9f9f9;
border-radius: 8px;
}
.empty-state i {
font-size: 48px;
color: #ccc;
margin-bottom: 16px;
}
.empty-state p {
color: #666;
margin-bottom: 16px;
}
.btn-primary {
background-color: #4CAF50;
color: white;
padding: 10px 20px;
border-radius: 4px;
text-decoration: none;
display: inline-block;
}
.btn-primary:hover {
background-color: #45a049;
}
.status-badge {
display: inline-block;
padding: 4px 8px;
border-radius: 12px;
font-size: 0.9em;
font-weight: 500;
}
.status-active {
background-color: #dff0d8;
color: #3c763d;
}
.status-inactive {
background-color: #f2dede;
color: #a94442;
}
.product-image {
width: 50px;
height: 50px;
object-fit: cover;
border-radius: 4px;
}
</style>
</head>
<body>
<?php include '../includes/header.php'; ?>
<div class="container admin-dashboard">
<h1>Admin Dashboard</h1>
<div class="stats-grid">
<div class="stat-card">
<h3>Total Users</h3>
<div class="number"><?php echo $total_users; ?></div>
</div>
<div class="stat-card">
<h3>Total Products</h3>
<div class="number"><?php echo $total_products; ?></div>
</div>
<div class="stat-card">
<h3>Total Orders</h3>
<div class="number"><?php echo $total_orders; ?></div>
</div>
</div>
<div class="admin-actions">
<a href="/unimarket/admin/users/manage.php" class="admin-action-btn">
<i class="fas fa-users"></i>
<span>Manage Users</span>
</a>
<a href="/unimarket/admin/products/index.php" class="admin-action-btn">
<i class="fas fa-box"></i>
<span>Manage Products</span>
</a>
<a href="/unimarket/admin/orders/manage.php" class="admin-action-btn">
<i class="fas fa-shopping-cart"></i> Manage Orders
</a>
<a href="/unimarket/admin/products/add.php" class="admin-action-btn">
<i class="fas fa-plus"></i> Add My Product
</a>
</div>
<div class="recent-section">
<div class="section-header">
<h2>My Products</h2>
<a href="/unimarket/admin/products/add.php" class="btn-add">
<i class="fas fa-plus"></i> Add New Product
</a>
</div>
<?php if (empty($admin_products)): ?>
<div class="empty-state">
<i class="fas fa-box-open"></i>
<p>You haven't added any products yet.</p>
<a href="/unimarket/admin/products/add.php" class="btn-primary">Add Your First Product</a>
</div>
<?php else: ?>
<table>
<thead>
<tr>
<th>Image</th>
<th>Name</th>
<th>Price</th>
<th>Stock</th>
<th>Status</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<?php foreach ($admin_products as $product): ?>
<tr>
<td>
<?php if (!empty($product['image_path'])): ?>
<img src="/unimarket/uploads/products/<?php echo htmlspecialchars($product['image_path']); ?>" alt="Product" class="product-image">
<?php else: ?>
<img src="/unimarket/assets/images/no-image.png" alt="No Image" class="product-image">
<?php endif; ?>
</td>
<td><?php echo htmlspecialchars($product['name']); ?></td>
<td>₱<?php echo number_format($product['price'], 2); ?></td>
<td><?php echo $product['stock_quantity']; ?></td>
<td>
<span class="status-badge <?php echo $product['stock_quantity'] > 0 ? 'status-active' : 'status-inactive'; ?>">
<?php echo $product['stock_quantity'] > 0 ? 'Active' : 'Inactive'; ?>
</span>
</td>
<td>
<a href="edit_product.php?id=<?php echo $product['product_id']; ?>" class="btn-edit">
<i class="fas fa-edit"></i> Edit
</a>
<a href="/unimarket/admin/products/delete.php?id=<?php echo $product['product_id']; ?>" class="btn-delete" onclick="return confirm('Are you sure you want to delete this product?')">
<i class="fas fa-trash"></i> Delete
</a>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<?php endif; ?>
</div>
<div class="recent-section">
<h2>Recent Users</h2>
<table>
<thead>
<tr>
<th>Username</th>
<th>Email</th>
<th>Role</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<?php foreach ($recent_users as $user): ?>
<tr>
<td><?php echo htmlspecialchars($user['username']); ?></td>
<td><?php echo htmlspecialchars($user['email']); ?></td>
<td><?php echo htmlspecialchars($user['role']); ?></td>
<td>
<a href="/unimarket/admin/users/edit.php?id=<?php echo $user['user_id']; ?>" class="btn-small btn-edit">
<i class="fas fa-edit"></i> Edit
</a>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<div class="recent-section">
<h2>Recent Orders</h2>
<table>
<thead>
<tr>
<th>Order ID</th>
<th>Customer</th>
<th>Date</th>
<th>Amount</th>
<th>Status</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<?php foreach ($recent_orders as $order): ?>
<tr>
<td>#<?php echo $order['order_id']; ?></td>
<td><?php echo htmlspecialchars($order['customer_name']); ?></td>
<td><?php echo date('M d, Y', strtotime($order['order_date'])); ?></td>
<td>₱<?php echo number_format($order['total_amount'], 2); ?></td>
<td><?php echo ucfirst($order['status']); ?></td>
<td>
<a href="/unimarket/admin/orders/view.php?id=<?php echo $order['order_id']; ?>" class="btn-small btn-view">
<i class="fas fa-eye"></i> View
</a>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
</div>
<?php include '../includes/footer.php'; ?>
</body>
</html>
+563
View File
@@ -0,0 +1,563 @@
<?php
require_once '../includes/auth_functions.php';
redirectIfNotLoggedIn();
redirectIfNotAdmin();
require_once '../config/database.php';
$success_message = '';
$error_message = '';
// Get product ID from URL
$product_id = isset($_GET['id']) ? (int)$_GET['id'] : 0;
// Fetch product details
$stmt = $pdo->prepare("SELECT * FROM products WHERE product_id = ? AND owner_id = ?");
$stmt->execute([$product_id, $_SESSION['user_id']]);
$product = $stmt->fetch();
if (!$product) {
header('Location: dashboard.php');
exit;
}
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$name = trim($_POST['name']);
$description = trim($_POST['description']);
$price = trim($_POST['price']);
$category = trim($_POST['category']);
$subcategory = trim($_POST['subcategory']);
$stock_quantity = (int)$_POST['stock_quantity'];
// Get size data if category is clothes
$size_data = null;
if ($category === 'clothes' && isset($_POST['sizes']) && isset($_POST['size_quantities'])) {
$sizes = $_POST['sizes'];
$quantities = $_POST['size_quantities'];
$size_data = [];
for ($i = 0; $i < count($sizes); $i++) {
if (!empty($sizes[$i]) && isset($quantities[$i]) && $quantities[$i] > 0) {
$size_data[$sizes[$i]] = (int)$quantities[$i];
}
}
// Update stock quantity based on sizes
$stock_quantity = array_sum($size_data);
}
// Validate inputs
if (empty($name) || empty($description) || empty($price) || empty($stock_quantity)) {
$error_message = 'Please fill in all required fields.';
} elseif (!is_numeric($price) || $price <= 0) {
$error_message = 'Please enter a valid price.';
} elseif ($stock_quantity < 0) {
$error_message = 'Stock quantity cannot be negative.';
} elseif ($category === 'clothes' && empty($size_data)) {
$error_message = 'Please add at least one size with quantity.';
} else {
try {
// Handle image upload
$image_path = $product['image_path']; // Keep existing image by default
if (isset($_FILES['image']) && $_FILES['image']['error'] === UPLOAD_ERR_OK) {
$upload_dir = '../uploads/products/';
if (!file_exists($upload_dir)) {
mkdir($upload_dir, 0777, true);
}
$file_extension = strtolower(pathinfo($_FILES['image']['name'], PATHINFO_EXTENSION));
$allowed_extensions = ['jpg', 'jpeg', 'png', 'gif'];
if (!in_array($file_extension, $allowed_extensions)) {
throw new Exception('Invalid file type. Only JPG, JPEG, PNG, and GIF files are allowed.');
}
$image_path = uniqid() . '.' . $file_extension;
$upload_path = $upload_dir . $image_path;
if (!move_uploaded_file($_FILES['image']['tmp_name'], $upload_path)) {
throw new Exception('Failed to upload image.');
}
// Delete old image if exists
if ($product['image_path'] && file_exists($upload_dir . $product['image_path'])) {
unlink($upload_dir . $product['image_path']);
}
}
if (empty($error_message)) {
$pdo->beginTransaction();
$stmt = $pdo->prepare("
UPDATE products
SET name = ?, description = ?, price = ?, category = ?, Sub_Cat = ?,
stock_quantity = ?, image_path = ?, size_data = ?
WHERE product_id = ? AND owner_id = ?
");
$stmt->execute([
$name,
$description,
$price,
$category,
$subcategory,
$stock_quantity,
$image_path,
$size_data ? json_encode($size_data) : null,
$product_id,
$_SESSION['user_id']
]);
$pdo->commit();
$success_message = 'Product updated successfully!';
// Refresh product data
$stmt = $pdo->prepare("SELECT * FROM products WHERE product_id = ? AND owner_id = ?");
$stmt->execute([$product_id, $_SESSION['user_id']]);
$product = $stmt->fetch();
}
} catch (Exception $e) {
$pdo->rollBack();
$error_message = $e->getMessage();
}
}
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Edit Product - UniMarket</title>
<link rel="stylesheet" href="/unimarket/assets/css/style.css">
<link rel="stylesheet" href="/unimarket/assets/css/responsive.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/css/all.min.css">
<style>
.edit-product {
max-width: 800px;
margin: 0 auto;
padding: 20px;
}
.form-group {
margin-bottom: 20px;
}
.form-group label {
display: block;
margin-bottom: 5px;
font-weight: 500;
}
.form-group input[type="text"],
.form-group input[type="number"],
.form-group textarea,
.form-group select {
width: 100%;
padding: 8px;
border: 1px solid #ddd;
border-radius: 4px;
font-size: 16px;
}
.form-group textarea {
height: 150px;
resize: vertical;
}
.btn-submit {
background-color: #4CAF50;
color: white;
padding: 12px 24px;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 16px;
}
.btn-submit:hover {
background-color: #45a049;
}
.alert {
padding: 15px;
margin-bottom: 20px;
border-radius: 4px;
}
.alert-success {
background-color: #dff0d8;
color: #3c763d;
border: 1px solid #d6e9c6;
}
.alert-error {
background-color: #f2dede;
color: #a94442;
border: 1px solid #ebccd1;
}
.back-button {
display: inline-flex;
align-items: center;
gap: 8px;
color: #6c757d;
text-decoration: none;
margin-bottom: 20px;
font-weight: 500;
transition: color 0.3s ease;
}
.back-button:hover {
color: #495057;
}
.back-button i {
font-size: 1.1rem;
}
.image-preview {
max-width: 200px;
margin-top: 10px;
}
.size-row {
display: flex;
gap: 10px;
margin-bottom: 10px;
align-items: center;
}
.size-select, .size-quantity {
padding: 8px;
border: 1px solid #ddd;
border-radius: 4px;
}
.size-select {
width: 100px;
}
.size-quantity {
width: 100px;
}
.btn-remove-size {
background: #ff4444;
color: white;
border: none;
border-radius: 4px;
width: 30px;
height: 30px;
cursor: pointer;
font-size: 18px;
display: flex;
align-items: center;
justify-content: center;
}
.btn-add-size {
background: var(--primary-color);
color: white;
border: none;
padding: 8px 16px;
border-radius: 4px;
cursor: pointer;
margin-top: 10px;
}
.btn-add-size:hover {
background: var(--primary-dark);
}
</style>
</head>
<body>
<?php include '../includes/header.php'; ?>
<div class="container edit-product">
<a href="dashboard.php" class="back-button">
<i class="fas fa-arrow-left"></i> Back to Dashboard
</a>
<h1>Edit Product</h1>
<?php if ($success_message): ?>
<div class="alert alert-success">
<?php echo $success_message; ?>
</div>
<?php endif; ?>
<?php if ($error_message): ?>
<div class="alert alert-error">
<?php echo $error_message; ?>
</div>
<?php endif; ?>
<form method="POST" enctype="multipart/form-data">
<div class="form-group">
<label for="name">Product Name*</label>
<input type="text" id="name" name="name" value="<?php echo htmlspecialchars($product['name']); ?>" required>
</div>
<div class="form-group">
<label for="description">Description*</label>
<textarea id="description" name="description" required><?php echo htmlspecialchars($product['description']); ?></textarea>
</div>
<div class="form-group">
<label for="price">Price*</label>
<input type="number" id="price" name="price" step="0.01" min="0" value="<?php echo htmlspecialchars($product['price']); ?>" required>
</div>
<div class="form-group">
<label for="category">Category*</label>
<select id="category" name="category" onchange="updateSubcategories()" required>
<option value="">Select a category</option>
<option value="clothes" <?php echo $product['category'] === 'clothes' ? 'selected' : ''; ?>>Clothes</option>
<option value="school_supplies" <?php echo $product['category'] === 'school_supplies' ? 'selected' : ''; ?>>School Supplies</option>
<option value="others" <?php echo $product['category'] === 'others' ? 'selected' : ''; ?>>Others</option>
</select>
</div>
<div class="form-group" id="subcategoryGroup" style="display: none;">
<label for="subcategory">Subcategory*</label>
<select id="subcategory" name="subcategory" onchange="handleSubcategoryChange()" required>
<option value="">Select a subcategory</option>
</select>
<input type="text" id="customSubcategory" name="custom_subcategory" style="display: none;" placeholder="Enter subcategory">
</div>
<div class="form-group" id="sizeGroup" style="display: <?php echo $product['category'] === 'clothes' ? 'block' : 'none'; ?>;">
<label>Sizes and Quantities*</label>
<div id="sizeContainer">
<?php
$size_data = json_decode($product['size_data'], true);
if ($size_data) {
foreach ($size_data as $size => $quantity) {
echo '<div class="size-row">';
echo '<select class="size-select" name="sizes[]">';
echo '<option value="XS" ' . ($size === 'XS' ? 'selected' : '') . '>XS</option>';
echo '<option value="S" ' . ($size === 'S' ? 'selected' : '') . '>S</option>';
echo '<option value="M" ' . ($size === 'M' ? 'selected' : '') . '>M</option>';
echo '<option value="L" ' . ($size === 'L' ? 'selected' : '') . '>L</option>';
echo '<option value="XL" ' . ($size === 'XL' ? 'selected' : '') . '>XL</option>';
echo '</select>';
echo '<input type="number" class="size-quantity" name="size_quantities[]" min="0" value="' . $quantity . '" required>';
echo '<button type="button" class="btn-remove-size" onclick="removeSize(this)">×</button>';
echo '</div>';
}
}
?>
</div>
<button type="button" class="btn-add-size" onclick="addSize()">Add Size</button>
</div>
<div class="form-group" id="stockGroup" style="display: <?php echo $product['category'] === 'clothes' ? 'none' : 'block'; ?>;">
<label for="stock_quantity">Stock Quantity*</label>
<input type="number" id="stock_quantity" name="stock_quantity" min="0" value="<?php echo htmlspecialchars($product['stock_quantity']); ?>" required>
</div>
<div class="form-group">
<label for="image">Product Image</label>
<?php if ($product['image_path']): ?>
<img src="/unimarket/uploads/products/<?php echo htmlspecialchars($product['image_path']); ?>" class="image-preview" style="display: block;">
<?php endif; ?>
<input type="file" id="image" name="image" accept="image/*" onchange="previewImage(this)">
<img id="imagePreview" class="image-preview">
</div>
<button type="submit" class="btn-submit">Update Product</button>
</form>
</div>
<script>
const subcategories = {
clothes: [
'T-Shirts',
'Pants',
'PE Uniform',
'University Uniform'
],
school_supplies: [
'Notebooks',
'Pens',
'Pencils',
'Highlighters',
'Markers',
'Art Supplies',
'Paper',
'Folders',
'Binders',
'Staplers'
],
others: [
'University Lace',
'Hygiene Products',
'Seals',
'Calculator',
'ID Holders'
]
};
function updateSubcategories() {
const categorySelect = document.getElementById('category');
const subcategorySelect = document.getElementById('subcategory');
const subcategoryGroup = document.getElementById('subcategoryGroup');
const customSubcategory = document.getElementById('customSubcategory');
const sizeGroup = document.getElementById('sizeGroup');
const stockQuantityInput = document.getElementById('stock_quantity');
const selectedCategory = categorySelect.value;
// Clear current options
subcategorySelect.innerHTML = '<option value="">Select a subcategory</option>';
if (selectedCategory && subcategories[selectedCategory]) {
// Show subcategory group
subcategoryGroup.style.display = 'block';
subcategorySelect.style.display = 'block';
customSubcategory.style.display = 'none';
// Show/hide size group based on category
if (selectedCategory === 'clothes') {
sizeGroup.style.display = 'block';
stockQuantityInput.style.display = 'none';
stockQuantityInput.value = calculateTotalSizeQuantity();
} else {
sizeGroup.style.display = 'none';
stockQuantityInput.style.display = 'block';
}
// Add new options
subcategories[selectedCategory].forEach(subcategory => {
const option = document.createElement('option');
option.value = subcategory.toLowerCase().replace(/\s+/g, '_');
option.textContent = subcategory;
subcategorySelect.appendChild(option);
});
// Add custom option
const customOption = document.createElement('option');
customOption.value = 'custom';
customOption.textContent = 'Other (Type your own)';
subcategorySelect.appendChild(customOption);
} else {
// Hide subcategory group if no category selected
subcategoryGroup.style.display = 'none';
sizeGroup.style.display = 'none';
stockQuantityInput.style.display = 'block';
}
}
function handleSubcategoryChange() {
const subcategorySelect = document.getElementById('subcategory');
const customSubcategory = document.getElementById('customSubcategory');
if (subcategorySelect.value === 'custom') {
subcategorySelect.style.display = 'none';
customSubcategory.style.display = 'block';
customSubcategory.focus();
customSubcategory.required = true;
subcategorySelect.name = 'subcategory_placeholder';
customSubcategory.name = 'subcategory';
} else {
subcategorySelect.style.display = 'block';
customSubcategory.style.display = 'none';
customSubcategory.required = false;
subcategorySelect.name = 'subcategory';
customSubcategory.name = 'custom_subcategory';
}
}
function calculateTotalSizeQuantity() {
const quantities = document.getElementsByName('size_quantities[]');
let total = 0;
for (let input of quantities) {
total += parseInt(input.value) || 0;
}
return total;
}
function updateTotalQuantity() {
const stockQuantityInput = document.getElementById('stock_quantity');
stockQuantityInput.value = calculateTotalSizeQuantity();
}
function addSize() {
const container = document.getElementById('sizeContainer');
const row = document.createElement('div');
row.className = 'size-row';
const sizeSelect = document.createElement('select');
sizeSelect.className = 'size-select';
sizeSelect.name = 'sizes[]';
['XS', 'S', 'M', 'L', 'XL'].forEach(size => {
const option = document.createElement('option');
option.value = size;
option.textContent = size;
sizeSelect.appendChild(option);
});
const quantityInput = document.createElement('input');
quantityInput.type = 'number';
quantityInput.className = 'size-quantity';
quantityInput.name = 'size_quantities[]';
quantityInput.min = '0';
quantityInput.required = true;
const removeButton = document.createElement('button');
removeButton.type = 'button';
removeButton.className = 'btn-remove-size';
removeButton.textContent = '×';
removeButton.onclick = function() { removeSize(this); };
row.appendChild(sizeSelect);
row.appendChild(quantityInput);
row.appendChild(removeButton);
container.appendChild(row);
}
function removeSize(button) {
button.parentElement.remove();
}
function previewImage(input) {
const preview = document.getElementById('imagePreview');
if (input.files && input.files[0]) {
const reader = new FileReader();
reader.onload = function(e) {
preview.src = e.target.result;
preview.style.display = 'block';
}
reader.readAsDataURL(input.files[0]);
} else {
preview.style.display = 'none';
}
}
// Initialize form state
document.addEventListener('DOMContentLoaded', function() {
updateSubcategories();
const subcategorySelect = document.getElementById('subcategory');
const currentSubcategory = '<?php echo htmlspecialchars($product['Sub_Cat']); ?>';
// Check if current subcategory is in the list
let found = false;
for (let option of subcategorySelect.options) {
if (option.value === currentSubcategory) {
option.selected = true;
found = true;
break;
}
}
// If not found, it's a custom subcategory
if (!found) {
subcategorySelect.value = 'custom';
handleSubcategoryChange();
document.getElementById('customSubcategory').value = currentSubcategory;
}
});
</script>
</body>
</html>
+266
View File
@@ -0,0 +1,266 @@
<?php
require_once '../../includes/auth_functions.php';
redirectIfNotLoggedIn();
redirectIfNotAdmin();
require_once '../../config/database.php';
// Handle order status updates
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['update_status'])) {
$order_id = $_POST['order_id'];
$new_status = $_POST['status'];
$stmt = $pdo->prepare("UPDATE orders SET status = ? WHERE order_id = ?");
$stmt->execute([$new_status, $order_id]);
flashMessage('success', 'Order status updated successfully.');
header("Location: manage.php");
exit();
}
// Get all orders with customer information
$stmt = $pdo->prepare("
SELECT o.*, u.username as customer_name
FROM orders o
JOIN users u ON o.customer_id = u.user_id
ORDER BY o.order_date DESC
");
$stmt->execute();
$orders = $stmt->fetchAll();
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Manage Orders - UniMarket Admin</title>
<link rel="stylesheet" href="/unimarket/assets/css/style.css">
<link rel="stylesheet" href="/unimarket/assets/css/responsive.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/css/all.min.css">
<style>
.manage-orders {
padding: 20px;
}
.orders-table {
width: 100%;
background: white;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
margin-top: 20px;
}
.orders-table table {
width: 100%;
border-collapse: collapse;
}
.orders-table th,
.orders-table td {
padding: 15px;
text-align: left;
border-bottom: 1px solid #ddd;
}
.orders-table th {
background-color: #f5f5f5;
font-weight: 600;
}
.orders-table tr:hover {
background-color: #f9f9f9;
}
.status-select {
padding: 5px;
border-radius: 4px;
border: 1px solid #ddd;
}
.btn-small {
padding: 5px 10px;
border-radius: 4px;
text-decoration: none;
font-size: 0.9em;
margin-right: 5px;
}
.btn-update {
background: var(--primary-color);
color: white;
border: none;
cursor: pointer;
}
.btn-update:hover {
background: var(--primary-dark);
}
.btn-view {
background-color: #28a745;
color: white;
border: none;
transition: background-color 0.3s ease;
}
.btn-view:hover {
background-color: #218838;
}
.alert {
padding: 15px;
border-radius: 4px;
margin-bottom: 20px;
}
.alert-success {
background-color: #d4edda;
color: #155724;
border: 1px solid #c3e6cb;
}
.status-badge {
padding: 5px 10px;
border-radius: 15px;
font-size: 0.9em;
font-weight: 500;
}
.status-pending {
background-color: #fff3cd;
color: #856404;
}
.status-completed {
background-color: #d4edda;
color: #155724;
font-weight: 600;
}
.completed-note {
display: flex;
align-items: center;
gap: 8px;
color: #155724;
font-weight: 500;
padding: 5px 10px;
background-color: #d4edda;
border-radius: 15px;
}
.completed-note i {
font-size: 0.9rem;
}
.status-cancelled {
background-color: #f8d7da;
color: #721c24;
font-weight: 600;
}
.cancelled-note {
display: flex;
align-items: center;
gap: 8px;
color: #721c24;
font-weight: 500;
padding: 5px 10px;
background-color: #f8d7da;
border-radius: 15px;
}
.cancelled-note i {
font-size: 0.9rem;
}
.back-button {
display: inline-flex;
align-items: center;
gap: 8px;
color: #6c757d;
text-decoration: none;
margin-bottom: 20px;
font-weight: 500;
transition: color 0.3s ease;
}
.back-button:hover {
color: #495057;
}
.back-button i {
font-size: 1.1rem;
}
</style>
</head>
<body>
<?php include '../../includes/header.php'; ?>
<div class="container manage-orders">
<a href="/unimarket/admin/dashboard.php" class="back-button">
<i class="fas fa-arrow-left"></i> Back to Dashboard
</a>
<h1>Manage Orders</h1>
<?php if ($message = getFlashMessage('success')): ?>
<div class="alert alert-success"><?php echo $message; ?></div>
<?php endif; ?>
<div class="orders-table">
<table>
<thead>
<tr>
<th>Order ID</th>
<th>Customer</th>
<th>Date</th>
<th>Amount</th>
<th>Status</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<?php foreach ($orders as $order): ?>
<tr>
<td>#<?php echo $order['order_id']; ?></td>
<td><?php echo htmlspecialchars($order['customer_name']); ?></td>
<td><?php echo date('M d, Y H:i', strtotime($order['order_date'])); ?></td>
<td>$<?php echo number_format($order['total_amount'], 2); ?></td>
<td>
<?php if ($order['status'] === 'completed'): ?>
<div class="completed-note">
<i class="fas fa-check-circle"></i> Completed
</div>
<?php elseif ($order['status'] === 'cancelled'): ?>
<div class="cancelled-note">
<i class="fas fa-times-circle"></i> Cancelled
</div>
<?php else: ?>
<form method="POST" style="display: inline;">
<input type="hidden" name="order_id" value="<?php echo $order['order_id']; ?>">
<select name="status" class="status-select">
<option value="pending" <?php echo $order['status'] === 'pending' ? 'selected' : ''; ?>>Pending</option>
<option value="processing" <?php echo $order['status'] === 'processing' ? 'selected' : ''; ?>>Processing</option>
<option value="completed" <?php echo $order['status'] === 'completed' ? 'selected' : ''; ?>>Completed</option>
<option value="cancelled" <?php echo $order['status'] === 'cancelled' ? 'selected' : ''; ?>>Cancelled</option>
</select>
<button type="submit" name="update_status" class="btn-small btn-update">Update</button>
</form>
<?php endif; ?>
</td>
<td>
<a href="view.php?id=<?php echo $order['order_id']; ?>" class="btn-small btn-view">
<i class="fas fa-eye"></i> View Details
</a>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
</div>
<?php include '../../includes/footer.php'; ?>
</body>
</html>
+211
View File
@@ -0,0 +1,211 @@
<?php
require_once '../../includes/auth_functions.php';
redirectIfNotLoggedIn();
redirectIfNotAdmin();
require_once '../../config/database.php';
// Check if order ID is provided
if (!isset($_GET['id'])) {
header("Location: /unimarket/admin/orders/manage.php");
exit();
}
$order_id = (int)$_GET['id'];
// Get order details with customer information
$stmt = $pdo->prepare("
SELECT o.*, u.username as customer_name, u.email as customer_email
FROM orders o
JOIN users u ON o.customer_id = u.user_id
WHERE o.order_id = ?
");
$stmt->execute([$order_id]);
$order = $stmt->fetch();
if (!$order) {
header("Location: /unimarket/admin/orders/manage.php");
exit();
}
// Get order items
$stmt = $pdo->prepare("
SELECT oi.*, p.name as product_name, p.price as product_price
FROM order_items oi
JOIN products p ON oi.product_id = p.product_id
WHERE oi.order_id = ?
");
$stmt->execute([$order_id]);
$order_items = $stmt->fetchAll();
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>View Order - UniMarket</title>
<link rel="stylesheet" href="/unimarket/assets/css/style.css">
<link rel="stylesheet" href="/unimarket/assets/css/responsive.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/css/all.min.css">
<style>
.order-details {
max-width: 800px;
margin: 30px auto;
padding: 20px;
}
.order-info {
background: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
margin-bottom: 20px;
}
.order-info h2 {
margin: 0 0 20px 0;
color: var(--primary-dark);
}
.info-group {
margin-bottom: 15px;
}
.info-group label {
font-weight: 600;
display: block;
margin-bottom: 5px;
}
.info-group span {
color: #666;
}
.order-items {
background: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
.order-items h2 {
margin: 0 0 20px 0;
color: var(--primary-dark);
}
table {
width: 100%;
border-collapse: collapse;
}
th, td {
padding: 12px;
text-align: left;
border-bottom: 1px solid #ddd;
}
th {
background-color: #f5f5f5;
font-weight: 600;
}
.order-total {
text-align: right;
margin-top: 20px;
font-size: 1.2em;
font-weight: 600;
}
.status-badge {
display: inline-block;
padding: 5px 10px;
border-radius: 15px;
font-size: 0.9em;
font-weight: 600;
}
.status-pending {
background-color: #fff3cd;
color: #856404;
}
.status-processing {
background-color: #cce5ff;
color: #004085;
}
.status-completed {
background-color: #d4edda;
color: #155724;
}
.status-cancelled {
background-color: #f8d7da;
color: #721c24;
}
</style>
</head>
<body>
<?php include '../../includes/header.php'; ?>
<div class="container order-details">
<div class="order-info">
<h2>Order #<?php echo $order['order_id']; ?></h2>
<div class="info-group">
<label>Customer</label>
<span><?php echo htmlspecialchars($order['customer_name']); ?></span>
</div>
<div class="info-group">
<label>Email</label>
<span><?php echo htmlspecialchars($order['customer_email']); ?></span>
</div>
<div class="info-group">
<label>Order Date</label>
<span><?php echo date('F d, Y H:i', strtotime($order['order_date'])); ?></span>
</div>
<div class="info-group">
<label>Status</label>
<span class="status-badge status-<?php echo strtolower($order['status']); ?>">
<?php echo ucfirst($order['status']); ?>
</span>
</div>
</div>
<div class="order-items">
<h2>Order Items</h2>
<table>
<thead>
<tr>
<th>Product</th>
<th>Price</th>
<th>Quantity</th>
<th>Subtotal</th>
</tr>
</thead>
<tbody>
<?php foreach ($order_items as $item): ?>
<tr>
<td><?php echo htmlspecialchars($item['product_name']); ?></td>
<td>₱<?php echo number_format($item['product_price'], 2); ?></td>
<td><?php echo $item['quantity']; ?></td>
<td>₱<?php echo number_format($item['product_price'] * $item['quantity'], 2); ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<div class="order-total">
Total: ₱<?php echo number_format($order['total_amount'], 2); ?>
</div>
</div>
</div>
<?php include '../../includes/footer.php'; ?>
</body>
</html>
+538
View File
@@ -0,0 +1,538 @@
<?php
require_once '../../includes/auth_functions.php';
redirectIfNotLoggedIn();
redirectIfNotAdmin();
require_once '../../config/database.php';
$success_message = '';
$error_message = '';
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$name = trim($_POST['name']);
$description = trim($_POST['description']);
$price = trim($_POST['price']);
$category = trim($_POST['category']);
$subcategory = trim($_POST['subcategory']);
$stock_quantity = (int)$_POST['stock_quantity'];
// Get size data if category is clothes
$size_data = null;
if ($category === 'clothes' && isset($_POST['sizes']) && isset($_POST['size_quantities'])) {
$sizes = $_POST['sizes'];
$quantities = $_POST['size_quantities'];
$size_data = [];
for ($i = 0; $i < count($sizes); $i++) {
if (!empty($sizes[$i]) && isset($quantities[$i]) && $quantities[$i] > 0) {
$size_data[$sizes[$i]] = (int)$quantities[$i];
}
}
// Update stock quantity based on sizes
$stock_quantity = array_sum($size_data);
}
// Validate inputs
if (empty($name) || empty($description) || empty($price) || empty($stock_quantity)) {
$error_message = 'Please fill in all required fields.';
} elseif (!is_numeric($price) || $price <= 0) {
$error_message = 'Please enter a valid price.';
} elseif ($stock_quantity < 0) {
$error_message = 'Stock quantity cannot be negative.';
} elseif ($category === 'clothes' && empty($size_data)) {
$error_message = 'Please add at least one size with quantity.';
} else {
try {
// Handle image upload
$image_path = null;
if (isset($_FILES['image']) && $_FILES['image']['error'] === UPLOAD_ERR_OK) {
$upload_dir = '../../uploads/products/';
if (!file_exists($upload_dir)) {
mkdir($upload_dir, 0777, true);
}
$file_extension = strtolower(pathinfo($_FILES['image']['name'], PATHINFO_EXTENSION));
$allowed_extensions = ['jpg', 'jpeg', 'png', 'gif'];
if (!in_array($file_extension, $allowed_extensions)) {
throw new Exception('Invalid file type. Only JPG, JPEG, PNG, and GIF files are allowed.');
}
$image_path = uniqid() . '.' . $file_extension;
$upload_path = $upload_dir . $image_path;
if (!move_uploaded_file($_FILES['image']['tmp_name'], $upload_path)) {
throw new Exception('Failed to upload image.');
}
}
if (empty($error_message)) {
$pdo->beginTransaction();
// Get admin's user ID from session
$owner_id = $_SESSION['user_id'];
$stmt = $pdo->prepare("
INSERT INTO products
(owner_id, name, description, price, category, Sub_Cat, stock_quantity, image_path, size_data)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
");
$stmt->execute([
$owner_id,
$name,
$description,
$price,
$category,
$subcategory,
$stock_quantity,
$image_path,
$size_data ? json_encode($size_data) : null
]);
$pdo->commit();
$success_message = 'Product added successfully!';
// Clear form
$name = $description = $price = $category = $subcategory = '';
$stock_quantity = 0;
}
} catch (Exception $e) {
$pdo->rollBack();
$error_message = $e->getMessage();
}
}
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Add Product - UniMarket</title>
<link rel="stylesheet" href="/unimarket/assets/css/style.css">
<link rel="stylesheet" href="/unimarket/assets/css/responsive.css">
<style>
.add-product {
max-width: 800px;
margin: 0 auto;
padding: 20px;
}
.form-group {
margin-bottom: 20px;
}
.form-group label {
display: block;
margin-bottom: 5px;
font-weight: 500;
}
.form-group input[type="text"],
.form-group input[type="number"],
.form-group textarea,
.form-group select {
width: 100%;
padding: 8px;
border: 1px solid #ddd;
border-radius: 4px;
font-size: 16px;
}
.form-group textarea {
height: 150px;
resize: vertical;
}
.btn-submit {
background-color: #4CAF50;
color: white;
padding: 12px 24px;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 16px;
}
.btn-submit:hover {
background-color: #45a049;
}
.alert {
padding: 15px;
margin-bottom: 20px;
border-radius: 4px;
}
.alert-success {
background-color: #dff0d8;
color: #3c763d;
border: 1px solid #d6e9c6;
}
.alert-error {
background-color: #f2dede;
color: #a94442;
border: 1px solid #ebccd1;
}
.back-button {
display: inline-flex;
align-items: center;
gap: 8px;
color: #6c757d;
text-decoration: none;
margin-bottom: 20px;
font-weight: 500;
transition: color 0.3s ease;
}
.back-button:hover {
color: #495057;
}
.back-button i {
font-size: 1.1rem;
}
.image-preview {
max-width: 200px;
margin-top: 10px;
display: none;
}
.size-row {
display: flex;
gap: 10px;
margin-bottom: 10px;
align-items: center;
}
.size-select, .size-quantity {
padding: 8px;
border: 1px solid #ddd;
border-radius: 4px;
}
.size-select {
width: 100px;
}
.size-quantity {
width: 100px;
}
.btn-remove-size {
background: #ff4444;
color: white;
border: none;
border-radius: 4px;
width: 30px;
height: 30px;
cursor: pointer;
font-size: 18px;
display: flex;
align-items: center;
justify-content: center;
}
.btn-add-size {
background: var(--primary-color);
color: white;
border: none;
padding: 8px 16px;
border-radius: 4px;
cursor: pointer;
margin-top: 10px;
}
.btn-add-size:hover {
background: var(--primary-dark);
}
</style>
</head>
<body>
<?php include '../../includes/header.php'; ?>
<div class="container add-product">
<a href="index.php" class="back-button">
<i class="fas fa-arrow-left"></i> Back to Products
</a>
<h1>Add New Product</h1>
<?php if ($success_message): ?>
<div class="alert alert-success">
<?php echo $success_message; ?>
</div>
<?php endif; ?>
<?php if ($error_message): ?>
<div class="alert alert-error">
<?php echo $error_message; ?>
</div>
<?php endif; ?>
<form method="POST" enctype="multipart/form-data">
<div class="form-group">
<label for="name">Product Name *</label>
<input type="text" id="name" name="name" value="<?php echo isset($name) ? htmlspecialchars($name) : ''; ?>" required>
</div>
<div class="form-group">
<label for="description">Description *</label>
<textarea id="description" name="description" required><?php echo isset($description) ? htmlspecialchars($description) : ''; ?></textarea>
</div>
<div class="form-group">
<label for="price">Price (₱) *</label>
<input type="number" id="price" name="price" min="0" step="0.01" value="<?php echo isset($price) ? $price : ''; ?>" required>
</div>
<div class="form-group">
<label for="stock_quantity">Stock Quantity *</label>
<input type="number" id="stock_quantity" name="stock_quantity" min="0" value="<?php echo isset($stock_quantity) ? $stock_quantity : ''; ?>" required>
</div>
<div class="form-group">
<label for="category">Category*</label>
<select id="category" name="category" onchange="updateSubcategories()" required>
<option value="">Select a category</option>
<option value="clothes">Clothes</option>
<option value="school_supplies">School Supplies</option>
<option value="others">Others</option>
</select>
</div>
<div class="form-group" id="subcategoryGroup" style="display: none;">
<label for="subcategory">Subcategory*</label>
<select id="subcategory" name="subcategory" onchange="handleSubcategoryChange()" required>
<option value="">Select a subcategory</option>
</select>
<input type="text" id="customSubcategory" name="custom_subcategory" style="display: none;" placeholder="Enter subcategory">
</div>
<div class="form-group" id="sizeGroup" style="display: none;">
<label>Sizes and Quantities*</label>
<div id="sizeContainer">
<div class="size-row">
<select name="sizes[]" class="size-select">
<option value="">Select Size</option>
<option value="XS">XS</option>
<option value="S">S</option>
<option value="M">M</option>
<option value="L">L</option>
<option value="XL">XL</option>
<option value="XXL">XXL</option>
</select>
<input type="number" name="size_quantities[]" min="0" placeholder="Quantity" class="size-quantity">
<button type="button" class="btn-remove-size" onclick="removeSizeRow(this)">×</button>
</div>
</div>
<button type="button" class="btn-add-size" onclick="addSizeRow()">Add Another Size</button>
</div>
<div class="form-group">
<label for="image">Product Image</label>
<input type="file" id="image" name="image" accept="image/*" onchange="previewImage(this)">
<img id="imagePreview" class="image-preview" src="#" alt="Image Preview">
</div>
<button type="submit" class="btn-submit">Add Product</button>
</form>
</div>
<?php include '../../includes/footer.php'; ?>
<script>
function previewImage(input) {
const preview = document.getElementById('imagePreview');
if (input.files && input.files[0]) {
const reader = new FileReader();
reader.onload = function(e) {
preview.src = e.target.result;
preview.style.display = 'block';
}
reader.readAsDataURL(input.files[0]);
} else {
preview.style.display = 'none';
}
}
const subcategories = {
clothes: [
'T-Shirts',
'Pants',
'PE Uniform',
'University Uniform'
],
school_supplies: [
'Notebooks',
'Pens',
'Pencils',
'Highlighters',
'Markers',
'Art Supplies',
'Paper',
'Folders',
'Binders',
'Staplers'
],
others: [
'University Lace',
'Hygiene Products',
'Seals',
'Calculator',
'ID Holders'
]
};
function updateSubcategories() {
const categorySelect = document.getElementById('category');
const subcategorySelect = document.getElementById('subcategory');
const subcategoryGroup = document.getElementById('subcategoryGroup');
const customSubcategory = document.getElementById('customSubcategory');
const sizeGroup = document.getElementById('sizeGroup');
const stockQuantityInput = document.getElementById('stock_quantity');
const selectedCategory = categorySelect.value;
// Clear current options
subcategorySelect.innerHTML = '<option value="">Select a subcategory</option>';
if (selectedCategory && subcategories[selectedCategory]) {
// Show subcategory group
subcategoryGroup.style.display = 'block';
subcategorySelect.style.display = 'block';
customSubcategory.style.display = 'none';
// Show/hide size group based on category
if (selectedCategory === 'clothes') {
sizeGroup.style.display = 'block';
stockQuantityInput.style.display = 'none';
stockQuantityInput.value = calculateTotalSizeQuantity();
} else {
sizeGroup.style.display = 'none';
stockQuantityInput.style.display = 'block';
}
// Add new options
subcategories[selectedCategory].forEach(subcategory => {
const option = document.createElement('option');
option.value = subcategory.toLowerCase().replace(/\s+/g, '_');
option.textContent = subcategory;
subcategorySelect.appendChild(option);
});
// Add custom option
const customOption = document.createElement('option');
customOption.value = 'custom';
customOption.textContent = 'Other (Type your own)';
subcategorySelect.appendChild(customOption);
} else {
// Hide subcategory group if no category selected
subcategoryGroup.style.display = 'none';
sizeGroup.style.display = 'none';
stockQuantityInput.style.display = 'block';
}
}
function handleSubcategoryChange() {
const subcategorySelect = document.getElementById('subcategory');
const customSubcategory = document.getElementById('customSubcategory');
if (subcategorySelect.value === 'custom') {
subcategorySelect.style.display = 'none';
customSubcategory.style.display = 'block';
customSubcategory.focus();
customSubcategory.required = true;
subcategorySelect.name = 'subcategory_placeholder';
customSubcategory.name = 'subcategory';
} else {
subcategorySelect.style.display = 'block';
customSubcategory.style.display = 'none';
customSubcategory.required = false;
subcategorySelect.name = 'subcategory';
customSubcategory.name = 'custom_subcategory';
}
}
function addSizeRow() {
const container = document.getElementById('sizeContainer');
const newRow = document.createElement('div');
newRow.className = 'size-row';
newRow.innerHTML = `
<select name="sizes[]" class="size-select" onchange="updateTotalQuantity()">
<option value="">Select Size</option>
<option value="XS">XS</option>
<option value="S">S</option>
<option value="M">M</option>
<option value="L">L</option>
<option value="XL">XL</option>
<option value="XXL">XXL</option>
</select>
<input type="number" name="size_quantities[]" min="0" placeholder="Quantity" class="size-quantity" onchange="updateTotalQuantity()">
<button type="button" class="btn-remove-size" onclick="removeSizeRow(this)">×</button>
`;
container.appendChild(newRow);
}
function removeSizeRow(button) {
button.parentElement.remove();
updateTotalQuantity();
}
function calculateTotalSizeQuantity() {
const quantities = document.getElementsByName('size_quantities[]');
let total = 0;
for (let input of quantities) {
total += parseInt(input.value) || 0;
}
return total;
}
function updateTotalQuantity() {
const stockQuantityInput = document.getElementById('stock_quantity');
stockQuantityInput.value = calculateTotalSizeQuantity();
}
// Form submission validation
document.querySelector('form').addEventListener('submit', function(e) {
const category = document.getElementById('category').value;
const subcategorySelect = document.getElementById('subcategory');
const customSubcategory = document.getElementById('customSubcategory');
if (subcategorySelect.style.display === 'none' && !customSubcategory.value.trim()) {
e.preventDefault();
alert('Please enter a subcategory');
return;
}
if (category === 'clothes') {
const sizeSelects = document.getElementsByName('sizes[]');
const sizeQuantities = document.getElementsByName('size_quantities[]');
let hasValidSize = false;
for (let i = 0; i < sizeSelects.length; i++) {
if (sizeSelects[i].value && sizeQuantities[i].value > 0) {
hasValidSize = true;
break;
}
}
if (!hasValidSize) {
e.preventDefault();
alert('Please add at least one size with quantity');
return;
}
}
});
// Initialize subcategories on page load
document.addEventListener('DOMContentLoaded', function() {
updateSubcategories();
});
</script>
</body>
</html>
+46
View File
@@ -0,0 +1,46 @@
<?php
require_once '../../includes/auth_functions.php';
redirectIfNotLoggedIn();
redirectIfNotAdmin();
require_once '../../config/database.php';
// Check if product ID is provided
if (!isset($_GET['id'])) {
header('Location: /unimarket/admin/dashboard.php');
exit;
}
$product_id = $_GET['id'];
// Get product details to check ownership and get image path
$stmt = $pdo->prepare("SELECT * FROM products WHERE product_id = ? AND owner_id = ?");
$stmt->execute([$product_id, $_SESSION['user_id']]);
$product = $stmt->fetch();
if (!$product) {
header('Location: /unimarket/admin/dashboard.php');
exit;
}
try {
// Delete product image if exists
if (!empty($product['image_path'])) {
$image_path = '../../assets/images/products/' . $product['image_path'];
if (file_exists($image_path)) {
unlink($image_path);
}
}
// Delete product from database
$stmt = $pdo->prepare("DELETE FROM products WHERE product_id = ? AND owner_id = ?");
$stmt->execute([$product_id, $_SESSION['user_id']]);
// Redirect back to dashboard with success message
header('Location: /unimarket/admin/dashboard.php?message=Product deleted successfully');
exit;
} catch (PDOException $e) {
// Redirect back to dashboard with error message
header('Location: /unimarket/admin/dashboard.php?error=Failed to delete product');
exit;
}
+457
View File
@@ -0,0 +1,457 @@
<?php
require_once __DIR__ . '/../../config/database.php';
require_once __DIR__ . '/../../includes/auth_functions.php';
// Check if user is logged in and is an admin
if (!isAdmin()) {
header('Location: ../../login.php');
exit();
}
$success_message = '';
$error_message = '';
// Check if product ID is provided
if (!isset($_GET['id'])) {
header('Location: index.php');
exit;
}
$product_id = $_GET['id'];
// Get product details
$stmt = $pdo->prepare("SELECT * FROM products WHERE product_id = ?");
$stmt->execute([$product_id]);
$product = $stmt->fetch();
if (!$product) {
header('Location: index.php');
exit;
}
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$name = trim($_POST['name']);
$description = trim($_POST['description']);
$price = floatval($_POST['price']);
$stock = intval($_POST['stock']);
$category = trim($_POST['category']);
$subcategory = isset($_POST['subcategory']) ? trim($_POST['subcategory']) : '';
$size = isset($_POST['size']) ? trim($_POST['size']) : null;
// Validate input
if (empty($name) || empty($description) || $price <= 0 || $stock < 0) {
$error_message = "Please fill in all required fields correctly.";
} else {
// Handle image upload
$image = $product['image'];
if (isset($_FILES['image']) && $_FILES['image']['error'] === UPLOAD_ERR_OK) {
$upload_dir = '../../uploads/products/';
if (!file_exists($upload_dir)) {
mkdir($upload_dir, 0777, true);
}
$file_extension = strtolower(pathinfo($_FILES['image']['name'], PATHINFO_EXTENSION));
$allowed_extensions = ['jpg', 'jpeg', 'png', 'gif'];
if (in_array($file_extension, $allowed_extensions)) {
// Delete old image if exists
if (!empty($product['image'])) {
$old_image_path = $upload_dir . $product['image'];
if (file_exists($old_image_path)) {
unlink($old_image_path);
}
}
$image = uniqid() . '.' . $file_extension;
$upload_path = $upload_dir . $image;
if (!move_uploaded_file($_FILES['image']['tmp_name'], $upload_path)) {
$error_message = "Failed to upload image.";
}
} else {
$error_message = "Invalid image format. Allowed formats: JPG, JPEG, PNG, GIF";
}
}
if (empty($error_message)) {
try {
$stmt = $pdo->prepare("
UPDATE products
SET name = ?, description = ?, price = ?, stock = ?,
category = ?, subcategory = ?, size = ?, image = ?
WHERE product_id = ?
");
$stmt->execute([
$name,
$description,
$price,
$stock,
$category,
$subcategory,
$size,
$image,
$product_id
]);
$success_message = "Product updated successfully!";
// Refresh product data
$stmt = $pdo->prepare("SELECT * FROM products WHERE product_id = ?");
$stmt->execute([$product_id]);
$product = $stmt->fetch();
} catch (PDOException $e) {
$error_message = "Error updating product: " . $e->getMessage();
}
}
}
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Edit Product - Admin Dashboard</title>
<link rel="stylesheet" href="../../assets/css/style.css">
</head>
<body>
<?php include '../../includes/header.php'; ?>
<div class="content">
<a href="index.php" class="back-button">
<i class="fas fa-arrow-left"></i> Back to Products
</a>
<div class="header">
<h1>Edit Product</h1>
</div>
<?php if ($success_message): ?>
<div class="alert alert-success">
<?php echo $success_message; ?>
</div>
<?php endif; ?>
<?php if ($error_message): ?>
<div class="alert alert-danger">
<?php echo $error_message; ?>
</div>
<?php endif; ?>
<form method="POST" enctype="multipart/form-data" class="form-container">
<div class="form-group">
<label for="name">Product Name*</label>
<input type="text" id="name" name="name" value="<?php echo isset($product['name']) ? htmlspecialchars($product['name']) : ''; ?>" required>
</div>
<div class="form-group">
<label for="description">Description*</label>
<textarea id="description" name="description" required><?php echo isset($product['description']) ? htmlspecialchars($product['description']) : ''; ?></textarea>
</div>
<div class="form-group">
<label for="price">Price (₱)*</label>
<input type="number" id="price" name="price" min="0" step="0.01" value="<?php echo isset($product['price']) ? $product['price'] : ''; ?>" required>
</div>
<div class="form-group">
<label for="stock">Stock*</label>
<input type="number" id="stock" name="stock" min="0" value="<?php echo isset($product['stock']) ? $product['stock'] : ''; ?>" required>
</div>
<div class="form-group">
<label for="category">Category*</label>
<select id="category" name="category" required onchange="updateSubcategories()">
<option value="">Select a category</option>
<option value="clothes" <?php echo (isset($product['category']) && $product['category'] === 'clothes') ? 'selected' : ''; ?>>Clothes</option>
<option value="school_supplies" <?php echo (isset($product['category']) && $product['category'] === 'school_supplies') ? 'selected' : ''; ?>>School Supplies</option>
<option value="others" <?php echo (isset($product['category']) && $product['category'] === 'others') ? 'selected' : ''; ?>>Others</option>
</select>
</div>
<div class="form-group" id="subcategoryGroup" style="display: none;">
<label for="subcategory">Subcategory*</label>
<div class="subcategory-container">
<select id="subcategory" name="subcategory" onchange="handleSubcategoryChange()">
<option value="">Select a subcategory</option>
</select>
<div class="subcategory-divider">or</div>
<input type="text" id="customSubcategory" name="customSubcategory" placeholder="Type your own subcategory" value="<?php echo isset($product['subcategory']) ? htmlspecialchars($product['subcategory']) : ''; ?>">
</div>
</div>
<div class="form-group" id="sizeGroup" style="display: none;">
<label for="size">Size*</label>
<select id="size" name="size">
<option value="">Select a size</option>
<option value="XS" <?php echo (isset($product['size']) && $product['size'] === 'XS') ? 'selected' : ''; ?>>Extra Small (XS)</option>
<option value="S" <?php echo (isset($product['size']) && $product['size'] === 'S') ? 'selected' : ''; ?>>Small (S)</option>
<option value="M" <?php echo (isset($product['size']) && $product['size'] === 'M') ? 'selected' : ''; ?>>Medium (M)</option>
<option value="L" <?php echo (isset($product['size']) && $product['size'] === 'L') ? 'selected' : ''; ?>>Large (L)</option>
<option value="XL" <?php echo (isset($product['size']) && $product['size'] === 'XL') ? 'selected' : ''; ?>>Extra Large (XL)</option>
<option value="XXL" <?php echo (isset($product['size']) && $product['size'] === 'XXL') ? 'selected' : ''; ?>>Double Extra Large (XXL)</option>
</select>
</div>
<div class="form-group">
<label for="image">Product Image</label>
<?php if (isset($product['image']) && $product['image']): ?>
<div class="current-image">
<img src="../../uploads/products/<?php echo htmlspecialchars($product['image']); ?>" alt="Current product image" style="max-width: 200px;">
</div>
<?php endif; ?>
<input type="file" id="image" name="image" accept="image/*" onchange="previewImage(this)">
<img id="imagePreview" class="image-preview" src="#" alt="Image preview">
</div>
<div class="form-group">
<button type="submit" class="btn btn-primary">
<i class="fas fa-save"></i> Update Product
</button>
</div>
</form>
</div>
<script>
const subcategories = {
clothes: [
'T-Shirts',
'Pants',
'PE Uniform',
'University Uniform'
],
school_supplies: [
'Notebooks',
'Pens',
'Pencils',
'Highlighters',
'Markers',
'Art Supplies',
'Paper',
'Folders',
'Binders',
'Staplers'
],
others: [
'University Lace',
'Hygiene Products',
'Seals',
'Calculator',
'ID Holders'
]
};
function updateSubcategories() {
const categorySelect = document.getElementById('category');
const subcategorySelect = document.getElementById('subcategory');
const subcategoryGroup = document.getElementById('subcategoryGroup');
const sizeGroup = document.getElementById('sizeGroup');
const customSubcategory = document.getElementById('customSubcategory');
const selectedCategory = categorySelect.value;
// Clear current options
subcategorySelect.innerHTML = '<option value="">Select a subcategory</option>';
if (selectedCategory && subcategories[selectedCategory]) {
// Show subcategory group
subcategoryGroup.style.display = 'block';
subcategorySelect.style.display = 'block';
customSubcategory.style.display = 'none';
// Show/hide size group based on category
if (selectedCategory === 'clothes') {
sizeGroup.style.display = 'block';
document.getElementById('size').required = true;
} else {
sizeGroup.style.display = 'none';
document.getElementById('size').required = false;
}
// Add new options
subcategories[selectedCategory].forEach(subcategory => {
const option = document.createElement('option');
option.value = subcategory.toLowerCase().replace(/\s+/g, '_');
option.textContent = subcategory;
// If this is the current subcategory, select it
if (option.value === '<?php echo isset($product['subcategory']) ? $product['subcategory'] : ''; ?>') {
option.selected = true;
}
subcategorySelect.appendChild(option);
});
// Add custom option
const customOption = document.createElement('option');
customOption.value = 'custom';
customOption.textContent = 'Other (Type your own)';
subcategorySelect.appendChild(customOption);
// If current subcategory is not in the list, show custom input
const currentSubcategory = '<?php echo isset($product['subcategory']) ? $product['subcategory'] : ''; ?>';
if (currentSubcategory && !subcategories[selectedCategory].some(sub => sub.toLowerCase().replace(/\s+/g, '_') === currentSubcategory)) {
subcategorySelect.value = 'custom';
handleSubcategoryChange();
}
} else {
// Hide subcategory group if no category selected
subcategoryGroup.style.display = 'none';
sizeGroup.style.display = 'none';
}
}
function handleSubcategoryChange() {
const subcategorySelect = document.getElementById('subcategory');
const customSubcategory = document.getElementById('customSubcategory');
if (subcategorySelect.value === 'custom') {
subcategorySelect.style.display = 'none';
customSubcategory.style.display = 'block';
customSubcategory.focus();
customSubcategory.required = true;
} else {
subcategorySelect.style.display = 'block';
customSubcategory.style.display = 'none';
customSubcategory.required = false;
}
}
function previewImage(input) {
const preview = document.getElementById('imagePreview');
if (input.files && input.files[0]) {
const reader = new FileReader();
reader.onload = function(e) {
preview.src = e.target.result;
preview.style.display = 'block';
}
reader.readAsDataURL(input.files[0]);
}
}
// Initialize subcategories on page load
document.addEventListener('DOMContentLoaded', function() {
updateSubcategories();
});
</script>
<style>
.back-button {
display: inline-flex;
align-items: center;
gap: 8px;
color: #6c757d;
text-decoration: none;
margin-bottom: 20px;
font-weight: 500;
transition: color 0.3s ease;
}
.back-button:hover {
color: #495057;
}
.back-button i {
font-size: 1.1rem;
}
.form-container {
max-width: 800px;
margin: 0 auto;
padding: 20px;
background: #fff;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
.form-group {
margin-bottom: 20px;
}
.form-group label {
display: block;
margin-bottom: 5px;
font-weight: 500;
}
.form-group input[type="text"],
.form-group input[type="number"],
.form-group textarea,
.form-group select {
width: 100%;
padding: 8px;
border: 1px solid #ddd;
border-radius: 4px;
font-size: 16px;
}
.form-group textarea {
height: 150px;
resize: vertical;
}
.subcategory-container {
display: flex;
align-items: center;
gap: 10px;
}
.subcategory-divider {
color: #666;
font-size: 14px;
}
.current-image {
margin: 10px 0;
}
.image-preview {
max-width: 200px;
margin-top: 10px;
display: none;
}
.form-actions {
display: flex;
gap: 10px;
margin-top: 20px;
}
.btn {
display: inline-flex;
align-items: center;
gap: 8px;
padding: 8px 16px;
border-radius: 4px;
text-decoration: none;
font-weight: 500;
transition: all 0.3s ease;
}
.btn i {
font-size: 14px;
}
.btn-primary {
background-color: #28a745;
color: white;
border: none;
}
.btn-primary:hover {
background-color: #218838;
transform: translateY(-1px);
}
.btn-secondary {
background-color: #6c757d;
color: white;
border: none;
}
.btn-secondary:hover {
background-color: #5a6268;
transform: translateY(-1px);
}
</style>
</body>
</html>
+331
View File
@@ -0,0 +1,331 @@
<?php
require_once __DIR__ . '/../../config/database.php';
require_once __DIR__ . '/../../includes/auth_functions.php';
// Check if user is logged in and is an admin
if (!isAdmin()) {
header('Location: ../../login.php');
exit();
}
// Start session if not already started
if (session_status() === PHP_SESSION_NONE) {
session_start();
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Product Management - Admin Dashboard</title>
<link rel="stylesheet" href="../../assets/css/style.css">
</head>
<body>
<?php include '../../includes/header.php'; ?>
<div class="content">
<div class="header">
<h1>Manage Products</h1>
</div>
<a href="../dashboard.php" class="back-button">
<i class="fas fa-arrow-left"></i> Back to Dashboard
</a>
<?php if (isset($_SESSION['success_message'])): ?>
<div class="alert alert-success">
<?php
echo $_SESSION['success_message'];
unset($_SESSION['success_message']);
?>
</div>
<?php endif; ?>
<?php if (isset($_SESSION['error_message'])): ?>
<div class="alert alert-danger">
<?php
echo $_SESSION['error_message'];
unset($_SESSION['error_message']);
?>
</div>
<?php endif; ?>
<?php
// Get all products with owner information
$stmt = $pdo->query("
SELECT p.*, u.username as owner_name
FROM products p
JOIN users u ON p.owner_id = u.user_id
ORDER BY u.username, p.created_at DESC
");
$products = $stmt->fetchAll();
// Debug information
echo "<div style='background: #f8f9fa; padding: 10px; margin: 10px 0; border-radius: 4px;'>";
echo "<h3>Debug Information:</h3>";
echo "<p>Total products found: " . count($products) . "</p>";
// Group products by owner
$grouped_products = [];
foreach ($products as $product) {
$owner_name = $product['owner_name'];
if (!isset($grouped_products[$owner_name])) {
$grouped_products[$owner_name] = [];
}
$grouped_products[$owner_name][] = $product;
}
echo "<p>Number of sellers: " . count($grouped_products) . "</p>";
echo "<p>Sellers found: " . implode(", ", array_keys($grouped_products)) . "</p>";
echo "</div>";
?>
<?php foreach ($grouped_products as $owner_name => $owner_products): ?>
<div class="seller-section">
<h2 class="seller-name"><?php echo htmlspecialchars($owner_name); ?>'s Products</h2>
<div class="table-responsive">
<table class="table">
<thead>
<tr>
<th>Image</th>
<th>Name</th>
<th>Category</th>
<th>Subcategory</th>
<th>Price</th>
<th>Stock</th>
<th>Status</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<?php foreach ($owner_products as $product): ?>
<tr>
<td>
<?php if (isset($product['image']) && !empty($product['image'])): ?>
<img src="../../uploads/products/<?php echo htmlspecialchars($product['image']); ?>" alt="<?php echo htmlspecialchars($product['name']); ?>" class="product-thumbnail">
<?php else: ?>
<div class="no-image">No Image</div>
<?php endif; ?>
</td>
<td><?php echo htmlspecialchars($product['name']); ?></td>
<td><?php echo htmlspecialchars($product['category']); ?></td>
<td><?php echo isset($product['subcategory']) ? htmlspecialchars($product['subcategory']) : ''; ?></td>
<td>₱<?php echo number_format($product['price'], 2); ?></td>
<td><?php echo isset($product['stock']) ? $product['stock'] : '0'; ?></td>
<td>
<span class="status-badge <?php echo isset($product['status']) ? $product['status'] : 'pending'; ?>">
<?php echo isset($product['status']) ? ucfirst($product['status']) : 'Pending'; ?>
</span>
</td>
<td>
<div class="action-buttons">
<a href="edit.php?id=<?php echo $product['product_id']; ?>" class="btn btn-edit">
<i class="fas fa-edit"></i> Edit
</a>
<a href="delete.php?id=<?php echo $product['product_id']; ?>" class="btn btn-delete" onclick="return confirm('Are you sure you want to delete this product?')">
<i class="fas fa-trash"></i> Delete
</a>
</div>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
</div>
<?php endforeach; ?>
</div>
<style>
.seller-section {
margin-bottom: 2rem;
background: #fff;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
padding: 1.5rem;
}
.seller-name {
color: #333;
font-size: 1.5rem;
margin-bottom: 1rem;
padding-bottom: 0.5rem;
border-bottom: 2px solid #4CAF50;
}
.table {
width: 100%;
border-collapse: collapse;
margin-top: 1rem;
}
.table th,
.table td {
padding: 1rem;
text-align: left;
border-bottom: 1px solid #eee;
}
.table th {
background-color: #f8f9fa;
font-weight: 600;
}
.product-thumbnail {
width: 50px;
height: 50px;
object-fit: cover;
border-radius: 4px;
}
.no-image {
width: 50px;
height: 50px;
background: #f8f9fa;
display: flex;
align-items: center;
justify-content: center;
border-radius: 4px;
font-size: 0.8rem;
color: #666;
}
.status-badge {
padding: 0.25rem 0.5rem;
border-radius: 4px;
font-size: 0.875rem;
font-weight: 500;
}
.status-badge.active {
background-color: #e8f5e9;
color: #2e7d32;
}
.status-badge.inactive {
background-color: #ffebee;
color: #c62828;
}
.action-buttons {
display: flex;
gap: 8px;
align-items: center;
}
.btn {
display: inline-flex;
align-items: center;
gap: 8px;
padding: 8px 16px;
border-radius: 4px;
text-decoration: none;
font-weight: 500;
transition: all 0.3s ease;
}
.btn i {
font-size: 14px;
}
.btn-primary {
background-color: #28a745;
color: white;
border: none;
}
.btn-primary:hover {
background-color: #218838;
transform: translateY(-1px);
}
.btn-secondary {
background-color: #6c757d;
color: white;
border: none;
}
.btn-secondary:hover {
background-color: #5a6268;
transform: translateY(-1px);
}
.btn-edit {
background-color: #28a745;
color: white;
border: none;
padding: 6px 12px;
border-radius: 4px;
text-decoration: none;
display: inline-flex;
align-items: center;
gap: 6px;
transition: all 0.3s ease;
}
.btn-edit:hover {
background-color: #218838;
color: white;
text-decoration: none;
transform: translateY(-1px);
}
.btn-delete {
background-color: #dc3545;
color: white;
border: none;
padding: 6px 12px;
border-radius: 4px;
text-decoration: none;
display: inline-flex;
align-items: center;
gap: 6px;
transition: all 0.3s ease;
}
.btn-delete:hover {
background-color: #c82333;
color: white;
text-decoration: none;
transform: translateY(-1px);
}
.alert {
padding: 1rem;
border-radius: 4px;
margin-bottom: 1rem;
}
.alert-success {
background-color: #e8f5e9;
color: #2e7d32;
border: 1px solid #c8e6c9;
}
.alert-danger {
background-color: #ffebee;
color: #c62828;
border: 1px solid #ffcdd2;
}
.back-button {
display: inline-flex;
align-items: center;
gap: 8px;
color: #6c757d;
text-decoration: none;
margin-bottom: 20px;
font-weight: 500;
transition: color 0.3s ease;
}
.back-button:hover {
color: #495057;
}
.back-button i {
font-size: 1.1rem;
}
</style>
</body>
</html>
+204
View File
@@ -0,0 +1,204 @@
<?php
require_once '../includes/auth_functions.php';
redirectIfNotLoggedIn();
redirectIfNotAdmin();
require_once '../config/database.php';
// Get total users by role
$stmt = $pdo->prepare("
SELECT role, COUNT(*) as count
FROM users
GROUP BY role
");
$stmt->execute();
$users_by_role = $stmt->fetchAll();
// Get total products by status
$stmt = $pdo->prepare("
SELECT status, COUNT(*) as count
FROM products
GROUP BY status
");
$stmt->execute();
$products_by_status = $stmt->fetchAll();
// Get total orders by status
$stmt = $pdo->prepare("
SELECT status, COUNT(*) as count
FROM orders
GROUP BY status
");
$stmt->execute();
$orders_by_status = $stmt->fetchAll();
// Get total revenue
$stmt = $pdo->prepare("
SELECT SUM(total_amount) as total
FROM orders
WHERE status = 'completed'
");
$stmt->execute();
$total_revenue = $stmt->fetchColumn();
// Get recent sales (last 7 days)
$stmt = $pdo->prepare("
SELECT DATE(order_date) as date, SUM(total_amount) as total
FROM orders
WHERE status = 'completed'
AND order_date >= DATE_SUB(CURDATE(), INTERVAL 7 DAY)
GROUP BY DATE(order_date)
ORDER BY date DESC
");
$stmt->execute();
$recent_sales = $stmt->fetchAll();
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Reports - UniMarket Admin</title>
<link rel="stylesheet" href="/unimarket/assets/css/style.css">
<link rel="stylesheet" href="/unimarket/assets/css/responsive.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/css/all.min.css">
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<style>
.reports {
padding: 20px;
}
.stats-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 20px;
margin-bottom: 30px;
}
.stat-card {
background: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
.stat-card h3 {
margin: 0 0 10px 0;
color: var(--primary-dark);
}
.stat-card .number {
font-size: 2em;
font-weight: bold;
color: var(--primary-color);
}
.chart-container {
background: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
margin-bottom: 20px;
}
.chart-container h2 {
margin: 0 0 20px 0;
color: var(--primary-dark);
}
.chart-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(400px, 1fr));
gap: 20px;
}
</style>
</head>
<body>
<?php include '../includes/header.php'; ?>
<div class="container reports">
<h1>System Reports</h1>
<div class="stats-grid">
<div class="stat-card">
<h3>Total Revenue</h3>
<div class="number">$<?php echo number_format($total_revenue, 2); ?></div>
</div>
<?php foreach ($users_by_role as $role): ?>
<div class="stat-card">
<h3>Total <?php echo ucfirst($role['role']); ?>s</h3>
<div class="number"><?php echo $role['count']; ?></div>
</div>
<?php endforeach; ?>
</div>
<div class="chart-grid">
<div class="chart-container">
<h2>Products by Status</h2>
<canvas id="productsChart"></canvas>
</div>
<div class="chart-container">
<h2>Orders by Status</h2>
<canvas id="ordersChart"></canvas>
</div>
</div>
<div class="chart-container">
<h2>Recent Sales (Last 7 Days)</h2>
<canvas id="salesChart"></canvas>
</div>
</div>
<?php include '../includes/footer.php'; ?>
<script>
// Products by Status Chart
new Chart(document.getElementById('productsChart'), {
type: 'pie',
data: {
labels: <?php echo json_encode(array_column($products_by_status, 'status')); ?>,
datasets: [{
data: <?php echo json_encode(array_column($products_by_status, 'count')); ?>,
backgroundColor: ['#4CAF50', '#FFC107', '#F44336']
}]
}
});
// Orders by Status Chart
new Chart(document.getElementById('ordersChart'), {
type: 'pie',
data: {
labels: <?php echo json_encode(array_column($orders_by_status, 'status')); ?>,
datasets: [{
data: <?php echo json_encode(array_column($orders_by_status, 'count')); ?>,
backgroundColor: ['#2196F3', '#FFC107', '#4CAF50', '#F44336']
}]
}
});
// Recent Sales Chart
new Chart(document.getElementById('salesChart'), {
type: 'line',
data: {
labels: <?php echo json_encode(array_column($recent_sales, 'date')); ?>,
datasets: [{
label: 'Daily Sales',
data: <?php echo json_encode(array_column($recent_sales, 'total')); ?>,
borderColor: '#2196F3',
tension: 0.1
}]
},
options: {
scales: {
y: {
beginAtZero: true
}
}
}
});
</script>
</body>
</html>
+206
View File
@@ -0,0 +1,206 @@
<?php
require_once '../../includes/auth_functions.php';
redirectIfNotLoggedIn();
redirectIfNotAdmin();
require_once '../../config/database.php';
// Check if user ID is provided
if (!isset($_GET['id'])) {
header("Location: /unimarket/admin/users/manage.php");
exit();
}
$user_id = (int)$_GET['id'];
// Get user details
$stmt = $pdo->prepare("SELECT * FROM users WHERE user_id = ?");
$stmt->execute([$user_id]);
$user = $stmt->fetch();
if (!$user) {
header("Location: /unimarket/admin/users/manage.php");
exit();
}
$error_message = '';
$success_message = '';
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$username = trim($_POST['username']);
$email = trim($_POST['email']);
$role = $_POST['role'];
// Validate inputs
if (empty($username) || empty($email)) {
$error_message = 'Please fill in all required fields.';
} else {
try {
// Check if username or email already exists (excluding current user)
$stmt = $pdo->prepare("
SELECT COUNT(*) FROM users
WHERE (username = ? OR email = ?)
AND user_id != ?
");
$stmt->execute([$username, $email, $user_id]);
if ($stmt->fetchColumn() > 0) {
$error_message = 'Username or email already exists.';
} else {
// Update user
$stmt = $pdo->prepare("
UPDATE users
SET username = ?, email = ?, role = ?
WHERE user_id = ?
");
$stmt->execute([$username, $email, $role, $user_id]);
if ($stmt->rowCount() > 0) {
$success_message = 'User updated successfully!';
// Refresh user data
$stmt = $pdo->prepare("SELECT * FROM users WHERE user_id = ?");
$stmt->execute([$user_id]);
$user = $stmt->fetch();
} else {
$error_message = 'No changes were made.';
}
}
} catch (PDOException $e) {
$error_message = 'Failed to update user. Please try again.';
}
}
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Edit User - UniMarket</title>
<link rel="stylesheet" href="/unimarket/assets/css/style.css">
<link rel="stylesheet" href="/unimarket/assets/css/responsive.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/css/all.min.css">
<style>
.edit-user-container {
max-width: 600px;
margin: 30px auto;
padding: 20px;
}
.form-group {
margin-bottom: 20px;
}
.form-group label {
display: block;
margin-bottom: 5px;
font-weight: 600;
}
.form-group input,
.form-group select {
width: 100%;
padding: 8px;
border: 1px solid #ddd;
border-radius: 4px;
}
.btn-submit {
background-color: #28a745;
color: white;
padding: 10px 20px;
border: none;
border-radius: 4px;
cursor: pointer;
font-weight: 500;
transition: background-color 0.3s ease;
}
.btn-submit:hover {
background-color: #218838;
}
.alert {
padding: 15px;
margin-bottom: 20px;
border-radius: 4px;
}
.alert-success {
background-color: #dff0d8;
color: #3c763d;
border: 1px solid #d6e9c6;
}
.alert-error {
background-color: #f2dede;
color: #a94442;
border: 1px solid #ebccd1;
}
.back-button {
display: inline-flex;
align-items: center;
gap: 8px;
color: #6c757d;
text-decoration: none;
margin-bottom: 20px;
font-weight: 500;
transition: color 0.3s ease;
}
.back-button:hover {
color: #495057;
}
.back-button i {
font-size: 1.1rem;
}
</style>
</head>
<body>
<?php include '../../includes/header.php'; ?>
<div class="container edit-user-container">
<a href="/unimarket/admin/users/manage.php" class="back-button">
<i class="fas fa-arrow-left"></i> Back to Users
</a>
<h1>Edit User</h1>
<?php if ($success_message): ?>
<div class="alert alert-success"><?php echo $success_message; ?></div>
<?php endif; ?>
<?php if ($error_message): ?>
<div class="alert alert-error"><?php echo $error_message; ?></div>
<?php endif; ?>
<form method="POST" class="card">
<div class="form-group">
<label for="username">Username</label>
<input type="text" id="username" name="username" value="<?php echo htmlspecialchars($user['username']); ?>" required>
</div>
<div class="form-group">
<label for="email">Email</label>
<input type="email" id="email" name="email" value="<?php echo htmlspecialchars($user['email']); ?>" required>
</div>
<div class="form-group">
<label for="role">Role</label>
<select id="role" name="role" required>
<option value="customer" <?php echo $user['role'] === 'customer' ? 'selected' : ''; ?>>Customer</option>
<option value="owner" <?php echo $user['role'] === 'owner' ? 'selected' : ''; ?>>Owner</option>
<option value="admin" <?php echo $user['role'] === 'admin' ? 'selected' : ''; ?>>Admin</option>
</select>
</div>
<button type="submit" class="btn-submit">Update User</button>
</form>
</div>
<?php include '../../includes/footer.php'; ?>
</body>
</html>
+261
View File
@@ -0,0 +1,261 @@
<?php
require_once '../../includes/auth_functions.php';
redirectIfNotLoggedIn();
redirectIfNotAdmin();
require_once '../../config/database.php';
// Handle user role updates
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['update_role'])) {
$user_id = $_POST['user_id'];
$new_role = $_POST['role'];
// Prevent changing own role
if ($user_id != $_SESSION['user_id']) {
$stmt = $pdo->prepare("UPDATE users SET role = ? WHERE user_id = ?");
$stmt->execute([$new_role, $user_id]);
flashMessage('success', 'User role updated successfully.');
} else {
flashMessage('error', 'You cannot change your own role.');
}
header("Location: manage.php");
exit();
}
// Get all users
$stmt = $pdo->prepare("SELECT * FROM users ORDER BY user_id DESC");
$stmt->execute();
$users = $stmt->fetchAll();
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Manage Users - UniMarket Admin</title>
<link rel="stylesheet" href="/unimarket/assets/css/style.css">
<link rel="stylesheet" href="/unimarket/assets/css/responsive.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/css/all.min.css">
<style>
.manage-users {
padding: 20px;
}
.users-table {
width: 100%;
background: white;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
margin-top: 20px;
}
.users-table table {
width: 100%;
border-collapse: collapse;
}
.users-table th,
.users-table td {
padding: 15px;
text-align: left;
border-bottom: 1px solid #ddd;
}
.users-table th {
background-color: #f5f5f5;
font-weight: 600;
}
.users-table tr:hover {
background-color: #f9f9f9;
}
.role-select {
padding: 5px;
border-radius: 4px;
border: 1px solid #ddd;
}
.btn-small {
padding: 5px 10px;
border-radius: 4px;
text-decoration: none;
font-size: 0.9em;
margin-right: 5px;
}
.btn-update {
background: var(--primary-color);
color: white;
border: none;
cursor: pointer;
}
.btn-update:hover {
background: var(--primary-dark);
}
.btn-edit {
background-color: #28a745;
color: white;
border: none;
}
.btn-edit:hover {
background-color: #218838;
}
.btn-delete {
background-color: #dc3545;
color: white;
border: none;
}
.btn-delete:hover {
background-color: #c82333;
}
.alert {
padding: 15px;
border-radius: 4px;
margin-bottom: 20px;
}
.alert-success {
background-color: #d4edda;
color: #155724;
border: 1px solid #c3e6cb;
}
.alert-error {
background-color: #f8d7da;
color: #721c24;
border: 1px solid #f5c6cb;
}
.role-badge {
display: inline-block;
padding: 5px 10px;
border-radius: 15px;
font-size: 0.9em;
font-weight: 500;
}
.role-customer {
background-color: #e2e3e5;
color: #383d41;
}
.role-owner {
background-color: #cce5ff;
color: #004085;
}
.role-admin {
background-color: #d4edda;
color: #155724;
}
.btn-edit.disabled {
background-color: #6c757d;
cursor: not-allowed;
opacity: 0.65;
}
.btn-edit.disabled:hover {
background-color: #6c757d;
transform: none;
}
.back-button {
display: inline-flex;
align-items: center;
gap: 8px;
color: #6c757d;
text-decoration: none;
margin-bottom: 20px;
font-weight: 500;
transition: color 0.3s ease;
}
.back-button:hover {
color: #495057;
}
.back-button i {
font-size: 1.1rem;
}
</style>
</head>
<body>
<?php include '../../includes/header.php'; ?>
<div class="container manage-users">
<a href="/unimarket/admin/dashboard.php" class="back-button">
<i class="fas fa-arrow-left"></i> Back to Dashboard
</a>
<h1>Manage Users</h1>
<?php if ($message = getFlashMessage('success')): ?>
<div class="alert alert-success"><?php echo $message; ?></div>
<?php endif; ?>
<?php if ($message = getFlashMessage('error')): ?>
<div class="alert alert-error"><?php echo $message; ?></div>
<?php endif; ?>
<div class="users-table">
<table>
<thead>
<tr>
<th>ID</th>
<th>Username</th>
<th>Email</th>
<th>Role</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<?php foreach ($users as $user): ?>
<tr>
<td><?php echo $user['user_id']; ?></td>
<td><?php echo htmlspecialchars($user['username']); ?></td>
<td><?php echo htmlspecialchars($user['email']); ?></td>
<td>
<?php
$role_class = 'role-' . $user['role'];
$role_display = ucfirst($user['role']);
?>
<span class="role-badge <?php echo $role_class; ?>">
<?php echo $role_display; ?>
</span>
</td>
<td>
<?php if ($user['role'] === 'admin'): ?>
<span class="btn-small btn-edit disabled">
<i class="fas fa-edit"></i> Edit
</span>
<?php else: ?>
<a href="edit.php?id=<?php echo $user['user_id']; ?>" class="btn-small btn-edit">
<i class="fas fa-edit"></i> Edit
</a>
<?php endif; ?>
<?php if ($user['user_id'] != $_SESSION['user_id']): ?>
<a href="delete.php?id=<?php echo $user['user_id']; ?>" class="btn-small btn-delete" onclick="return confirm('Are you sure you want to delete this user?')">
<i class="fas fa-trash"></i> Delete
</a>
<?php endif; ?>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
</div>
<?php include '../../includes/footer.php'; ?>
</body>
</html>
+132
View File
@@ -0,0 +1,132 @@
<?php
require_once '../includes/auth_functions.php';
require_once '../config/database.php';
// Enable error reporting for debugging
error_reporting(E_ALL);
ini_set('display_errors', 1);
// Check if user is logged in and is a customer
if (!isLoggedIn() || !isCustomer()) {
echo json_encode(['success' => false, 'message' => 'Please log in to add items to cart']);
exit();
}
// Check if required parameters are provided
if (!isset($_POST['product_id']) || !isset($_POST['quantity'])) {
echo json_encode(['success' => false, 'message' => 'Missing required parameters']);
exit();
}
$product_id = (int)$_POST['product_id'];
$quantity = (int)$_POST['quantity'];
$size = isset($_POST['size']) ? $_POST['size'] : null;
// Validate quantity
if ($quantity < 1) {
echo json_encode(['success' => false, 'message' => 'Quantity must be at least 1']);
exit();
}
try {
// Check product availability
$stmt = $pdo->prepare("SELECT stock_quantity, category, size_data FROM products WHERE product_id = ?");
$stmt->execute([$product_id]);
$product = $stmt->fetch();
if (!$product) {
echo json_encode(['success' => false, 'message' => 'Product not found']);
exit();
}
// Check if size is required for clothing items
if ($product['category'] === 'clothes' && empty($size)) {
echo json_encode(['success' => false, 'message' => 'Please select a size']);
exit();
}
// Validate size availability for clothing items
if ($product['category'] === 'clothes' && $product['size_data']) {
$size_data = json_decode($product['size_data'], true);
if (!isset($size_data[$size]) || $size_data[$size] < $quantity) {
echo json_encode(['success' => false, 'message' => "Selected size is not available in the requested quantity"]);
exit();
}
} else if ($quantity > $product['stock_quantity']) {
echo json_encode(['success' => false, 'message' => "Only {$product['stock_quantity']} available in stock"]);
exit();
}
// Get or create cart for user
$stmt = $pdo->prepare("SELECT cart_id FROM cart WHERE user_id = ?");
$stmt->execute([$_SESSION['user_id']]);
$cart = $stmt->fetch();
if (!$cart) {
$stmt = $pdo->prepare("INSERT INTO cart (user_id) VALUES (?)");
$stmt->execute([$_SESSION['user_id']]);
$cart_id = $pdo->lastInsertId();
} else {
$cart_id = $cart['cart_id'];
}
// Check if product already in cart
$stmt = $pdo->prepare("SELECT * FROM cart_items WHERE cart_id = ? AND product_id = ?");
$stmt->execute([$cart_id, $product_id]);
$existing_item = $stmt->fetch();
if ($existing_item) {
// Update quantity if already in cart
$new_quantity = $existing_item['quantity'] + $quantity;
if ($product['category'] === 'clothes' && $product['size_data']) {
$size_data = json_decode($product['size_data'], true);
if ($new_quantity > $size_data[$size]) {
echo json_encode(['success' => false, 'message' => "Cannot add more than available stock for selected size"]);
exit();
}
} else if ($new_quantity > $product['stock_quantity']) {
echo json_encode(['success' => false, 'message' => "Cannot add more than available stock"]);
exit();
}
$stmt = $pdo->prepare("UPDATE cart_items SET quantity = ? WHERE cart_item_id = ?");
$stmt->execute([$new_quantity, $existing_item['cart_item_id']]);
} else {
// Add new item to cart
try {
$stmt = $pdo->prepare("INSERT INTO cart_items (cart_id, product_id, quantity, size) VALUES (?, ?, ?, ?)");
$stmt->execute([$cart_id, $product_id, $quantity, $size]);
} catch (PDOException $e) {
// If the size column doesn't exist, try without it
if (strpos($e->getMessage(), 'Unknown column') !== false) {
$stmt = $pdo->prepare("INSERT INTO cart_items (cart_id, product_id, quantity) VALUES (?, ?, ?)");
$stmt->execute([$cart_id, $product_id, $quantity]);
} else {
throw $e;
}
}
}
// Get updated cart count
$stmt = $pdo->prepare("
SELECT SUM(quantity) as count
FROM cart_items ci
JOIN cart c ON ci.cart_id = c.cart_id
WHERE c.user_id = ?
");
$stmt->execute([$_SESSION['user_id']]);
$cart_count = $stmt->fetch()['count'] ?? 0;
echo json_encode([
'success' => true,
'message' => 'Product added to cart!',
'cart_count' => $cart_count
]);
} catch (PDOException $e) {
error_log("Add to cart error: " . $e->getMessage());
echo json_encode([
'success' => false,
'message' => 'Failed to add product to cart. Please try again.',
'debug' => $e->getMessage()
]);
}
+90
View File
@@ -0,0 +1,90 @@
<?php
require_once '../includes/auth_functions.php';
require_once '../config/database.php';
// Enable error reporting for debugging
error_reporting(E_ALL);
ini_set('display_errors', 1);
// Set proper content type
header('Content-Type: application/json');
// Check if user is logged in and is a customer
if (!isLoggedIn() || !isCustomer()) {
echo json_encode(['success' => false, 'message' => 'Please log in to remove items from cart']);
exit();
}
// Check if required parameters are provided
if (!isset($_POST['cart_item_id'])) {
echo json_encode(['success' => false, 'message' => 'Missing required parameters']);
exit();
}
$cart_item_id = (int)$_POST['cart_item_id'];
try {
// Verify the cart item belongs to the user
$stmt = $pdo->prepare("
SELECT ci.*
FROM cart_items ci
JOIN cart c ON ci.cart_id = c.cart_id
WHERE ci.cart_item_id = ? AND c.user_id = ?
");
$stmt->execute([$cart_item_id, $_SESSION['user_id']]);
$cart_item = $stmt->fetch();
if (!$cart_item) {
echo json_encode(['success' => false, 'message' => 'Cart item not found']);
exit();
}
// Delete the cart item
$stmt = $pdo->prepare("DELETE FROM cart_items WHERE cart_item_id = ?");
$result = $stmt->execute([$cart_item_id]);
if (!$result) {
throw new PDOException("Failed to remove cart item");
}
// Get updated cart total
$stmt = $pdo->prepare("
SELECT SUM(ci.quantity * p.price) as total
FROM cart_items ci
JOIN products p ON ci.product_id = p.product_id
JOIN cart c ON ci.cart_id = c.cart_id
WHERE c.user_id = ?
");
$stmt->execute([$_SESSION['user_id']]);
$cart_total = $stmt->fetch()['total'] ?? 0;
// Get updated cart count
$stmt = $pdo->prepare("
SELECT SUM(quantity) as count
FROM cart_items ci
JOIN cart c ON ci.cart_id = c.cart_id
WHERE c.user_id = ?
");
$stmt->execute([$_SESSION['user_id']]);
$cart_count = $stmt->fetch()['count'] ?? 0;
echo json_encode([
'success' => true,
'message' => 'Item removed from cart',
'total' => $cart_total,
'cart_count' => $cart_count
]);
} catch (PDOException $e) {
error_log("Database error: " . $e->getMessage());
echo json_encode([
'success' => false,
'message' => 'Failed to remove item from cart. Please try again.'
]);
} catch (Exception $e) {
error_log("General error: " . $e->getMessage());
echo json_encode([
'success' => false,
'message' => 'An unexpected error occurred. Please try again.'
]);
}
+115
View File
@@ -0,0 +1,115 @@
<?php
require_once '../includes/auth_functions.php';
require_once '../config/database.php';
// Enable error reporting for debugging
error_reporting(E_ALL);
ini_set('display_errors', 1);
// Set proper content type
header('Content-Type: application/json');
// Check if user is logged in and is a customer
if (!isLoggedIn() || !isCustomer()) {
echo json_encode(['success' => false, 'message' => 'Please log in to update cart']);
exit();
}
// Check if required parameters are provided
if (!isset($_POST['cart_item_id']) || !isset($_POST['quantity'])) {
echo json_encode(['success' => false, 'message' => 'Missing required parameters']);
exit();
}
$cart_item_id = (int)$_POST['cart_item_id'];
$quantity = (int)$_POST['quantity'];
// Log the received data
error_log("Received update request - Cart Item ID: $cart_item_id, Quantity: $quantity");
// Validate quantity
if ($quantity < 1) {
echo json_encode(['success' => false, 'message' => 'Quantity must be at least 1']);
exit();
}
try {
// Get current cart item and product details
$stmt = $pdo->prepare("
SELECT ci.*, p.stock_quantity, p.price, p.product_id
FROM cart_items ci
JOIN products p ON ci.product_id = p.product_id
JOIN cart c ON ci.cart_id = c.cart_id
WHERE ci.cart_item_id = ? AND c.user_id = ?
");
$stmt->execute([$cart_item_id, $_SESSION['user_id']]);
$cart_item = $stmt->fetch();
if (!$cart_item) {
echo json_encode(['success' => false, 'message' => 'Cart item not found']);
exit();
}
// Log the cart item details
error_log("Cart item found: " . print_r($cart_item, true));
// Get current stock quantity
$stmt = $pdo->prepare("SELECT stock_quantity FROM products WHERE product_id = ?");
$stmt->execute([$cart_item['product_id']]);
$current_stock = $stmt->fetch()['stock_quantity'];
// Check stock availability
if ($quantity > $current_stock) {
echo json_encode([
'success' => false,
'message' => "Only {$current_stock} available in stock",
'previous_quantity' => $cart_item['quantity'],
'current_stock' => $current_stock
]);
exit();
}
// Update quantity
$stmt = $pdo->prepare("UPDATE cart_items SET quantity = ? WHERE cart_item_id = ?");
$result = $stmt->execute([$quantity, $cart_item_id]);
if (!$result) {
throw new PDOException("Failed to update cart item");
}
// Get updated cart total
$stmt = $pdo->prepare("
SELECT SUM(ci.quantity * p.price) as total
FROM cart_items ci
JOIN products p ON ci.product_id = p.product_id
JOIN cart c ON ci.cart_id = c.cart_id
WHERE c.user_id = ?
");
$stmt->execute([$_SESSION['user_id']]);
$cart_total = $stmt->fetch()['total'] ?? 0;
// Log success
error_log("Cart updated successfully - New quantity: $quantity, Total: $cart_total");
echo json_encode([
'success' => true,
'message' => 'Quantity updated successfully',
'cart_total' => $cart_total,
'current_stock' => $current_stock
]);
} catch (PDOException $e) {
error_log("Database error: " . $e->getMessage());
echo json_encode([
'success' => false,
'message' => 'Failed to update quantity. Please try again.',
'previous_quantity' => $cart_item['quantity'] ?? 1
]);
} catch (Exception $e) {
error_log("General error: " . $e->getMessage());
echo json_encode([
'success' => false,
'message' => 'An unexpected error occurred. Please try again.',
'previous_quantity' => $cart_item['quantity'] ?? 1
]);
}
+48
View File
@@ -0,0 +1,48 @@
@media (max-width: 768px) {
.header-content {
flex-direction: column;
text-align: center;
}
nav ul {
margin-top: 15px;
justify-content: center;
}
nav ul li {
margin: 0 10px;
}
.dashboard-container {
grid-template-columns: 1fr;
}
.sidebar {
margin-bottom: 20px;
}
.product-grid {
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
}
}
@media (max-width: 480px) {
.container {
width: 95%;
padding: 10px;
}
.product-grid {
grid-template-columns: 1fr;
}
.btn {
padding: 8px 15px;
font-size: 14px;
}
.table {
display: block;
overflow-x: auto;
}
}
+466
View File
@@ -0,0 +1,466 @@
:root {
--primary-color: #2e7d32;
--primary-light: #60ad5e;
--primary-dark: #005005;
--secondary-color: #f5f5f5;
--text-color: #333;
--light-text: #fff;
--error-color: #d32f2f;
--success-color: #388e3c;
--warning-color: #f57c00;
--light-bg: #f5f5f5;
--white: #ffffff;
--border-color: #e0e0e0;
--shadow-color: rgba(0, 0, 0, 0.1);
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}
body {
background-color: #f9f9f9;
color: var(--text-color);
line-height: 1.6;
font-family: 'Roboto', sans-serif;
min-height: 100vh;
background: linear-gradient(135deg, #f5f7fa 0%, #e4e8eb 100%);
position: relative;
}
body::before {
content: '';
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background:
radial-gradient(circle at 20% 20%, rgba(46, 125, 50, 0.05) 0%, transparent 50%),
radial-gradient(circle at 80% 80%, rgba(76, 175, 80, 0.05) 0%, transparent 50%),
radial-gradient(circle at 50% 50%, rgba(27, 94, 32, 0.03) 0%, transparent 50%);
pointer-events: none;
z-index: -1;
}
.container {
width: 90%;
max-width: 1200px;
margin: 20px auto;
padding: 30px;
background: rgba(255, 255, 255, 0.95);
border-radius: 15px;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
backdrop-filter: blur(10px);
-webkit-backdrop-filter: blur(10px);
border: 1px solid rgba(255, 255, 255, 0.2);
}
.container:hover {
transform: none;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
}
header {
background-color: var(--primary-color);
color: var(--light-text);
padding: 15px 0;
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
}
.header-content {
display: flex;
justify-content: space-between;
align-items: center;
}
.logo {
font-size: 24px;
font-weight: bold;
}
nav ul {
display: flex;
list-style: none;
}
nav ul li {
margin-left: 20px;
}
nav ul li a {
color: var(--light-text);
text-decoration: none;
font-weight: 500;
}
nav ul li a:hover {
opacity: 1;
}
.btn {
display: inline-block;
padding: 12px 24px;
background: var(--primary-color);
color: white;
border: none;
border-radius: 8px;
cursor: pointer;
text-decoration: none;
font-size: 1rem;
font-weight: 500;
transition: all 0.3s ease;
position: relative;
overflow: hidden;
}
.btn::after {
content: '';
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: linear-gradient(45deg, transparent, rgba(255, 255, 255, 0.1), transparent);
transform: translateX(-100%);
transition: transform 0.6s ease;
}
.btn:hover {
background: var(--primary-dark);
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(46, 125, 50, 0.2);
}
.btn:hover::after {
transform: translateX(100%);
}
.btn-secondary {
background-color: #757575;
}
.btn-secondary:hover {
background-color: #616161;
}
.card {
background: rgba(255, 255, 255, 0.95);
border-radius: 12px;
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.08);
padding: 25px;
margin-bottom: 25px;
backdrop-filter: blur(5px);
-webkit-backdrop-filter: blur(5px);
border: 1px solid rgba(255, 255, 255, 0.2);
transition: transform 0.3s ease, box-shadow 0.3s ease;
}
.card:hover {
transform: translateY(-3px);
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12);
}
.form-group {
margin-bottom: 20px;
background: rgba(255, 255, 255, 0.8);
padding: 20px;
border-radius: 10px;
border: 1px solid rgba(255, 255, 255, 0.2);
transition: transform 0.3s ease;
}
.form-group:hover {
transform: translateY(-2px);
}
.form-group label {
display: block;
margin-bottom: 5px;
font-weight: 500;
}
input[type="text"],
input[type="email"],
input[type="password"],
input[type="number"],
input[type="tel"],
select,
textarea {
width: 100%;
padding: 12px;
border: 1px solid rgba(0, 0, 0, 0.1);
border-radius: 8px;
background: rgba(255, 255, 255, 0.9);
transition: all 0.3s ease;
font-size: 1rem;
}
input[type="text"]:focus,
input[type="email"]:focus,
input[type="password"]:focus,
input[type="number"]:focus,
input[type="tel"]:focus,
select:focus,
textarea:focus {
border-color: var(--primary-color);
box-shadow: 0 0 0 3px rgba(46, 125, 50, 0.2);
outline: none;
transform: translateY(-1px);
}
.alert {
padding: 15px 20px;
border-radius: 8px;
margin-bottom: 20px;
position: relative;
overflow: hidden;
backdrop-filter: blur(5px);
-webkit-backdrop-filter: blur(5px);
}
.alert::before {
content: '';
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: linear-gradient(45deg, transparent, rgba(255, 255, 255, 0.1), transparent);
transform: translateX(-100%);
animation: shimmer 2s infinite;
}
@keyframes shimmer {
100% {
transform: translateX(100%);
}
}
.alert-success {
background: rgba(56, 142, 60, 0.95);
color: white;
}
.alert-error {
background: rgba(211, 47, 47, 0.95);
color: white;
}
.product-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
gap: 25px;
padding: 20px 0;
}
.product-card {
background: rgba(255, 255, 255, 0.95);
border-radius: 12px;
overflow: hidden;
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.08);
transition: all 0.3s ease;
border: 1px solid rgba(255, 255, 255, 0.2);
}
.product-card:hover {
transform: translateY(-5px);
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12);
}
.product-image {
height: 200px;
background-color: #f5f5f5;
display: flex;
align-items: center;
justify-content: center;
overflow: hidden;
}
.product-image img {
max-width: 100%;
max-height: 100%;
object-fit: cover;
}
.product-info {
padding: 15px;
}
.product-title {
font-size: 18px;
margin-bottom: 10px;
}
.product-price {
font-weight: bold;
color: var(--primary-color);
font-size: 20px;
margin-bottom: 10px;
}
/* Dashboard styles */
.dashboard-container {
display: grid;
grid-template-columns: 250px 1fr;
gap: 20px;
}
.sidebar {
background-color: #fff;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
padding: 20px;
}
.sidebar ul {
list-style: none;
}
.sidebar ul li {
margin-bottom: 10px;
}
.sidebar ul li a {
color: var(--text-color);
text-decoration: none;
display: block;
padding: 8px 0;
transition: color 0.3s;
}
.sidebar ul li a:hover {
color: var(--primary-color);
}
.sidebar ul li a.active {
color: var(--primary-color);
font-weight: bold;
}
.main-content {
background-color: #fff;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
padding: 20px;
}
/* Table styles */
.table {
width: 100%;
border-collapse: collapse;
margin-top: 20px;
}
.table th,
.table td {
padding: 12px 15px;
text-align: left;
border-bottom: 1px solid #ddd;
}
.table th {
background-color: #f5f5f5;
font-weight: 600;
}
.table tr:hover {
background-color: #f9f9f9;
}
/* Size grid styles */
.size-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(80px, 1fr));
gap: 12px;
margin-top: 15px;
}
.size-item {
border: 1px solid rgba(0, 0, 0, 0.1);
border-radius: 8px;
padding: 10px;
text-align: center;
background: rgba(255, 255, 255, 0.9);
transition: all 0.3s ease;
}
.size-item.in-stock {
border-color: var(--success-color);
background: rgba(56, 142, 60, 0.1);
}
.size-item.out-of-stock {
border-color: var(--error-color);
background: rgba(211, 47, 47, 0.1);
opacity: 0.7;
}
.size-item:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
}
/* Custom scrollbar */
::-webkit-scrollbar {
width: 10px;
}
::-webkit-scrollbar-track {
background: rgba(0, 0, 0, 0.1);
border-radius: 5px;
}
::-webkit-scrollbar-thumb {
background: var(--primary-color);
border-radius: 5px;
border: 2px solid rgba(255, 255, 255, 0.8);
}
::-webkit-scrollbar-thumb:hover {
background: var(--primary-dark);
}
/* Responsive styles are in responsive.css */
/* Toast Notification Styles */
.toast-container {
position: fixed;
top: 20px;
right: 20px;
z-index: 1000;
}
.toast {
background: #333;
color: white;
padding: 12px 24px;
border-radius: 4px;
margin-bottom: 10px;
box-shadow: 0 2px 5px rgba(0,0,0,0.2);
display: flex;
align-items: center;
gap: 10px;
transform: translateX(120%);
transition: transform 0.3s ease-in-out;
min-width: 250px;
}
.toast.show {
transform: translateX(0);
}
.toast.success {
background: #28a745;
}
.toast.error {
background: #dc3545;
}
.toast i {
font-size: 1.2em;
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 169 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 MiB

+145
View File
@@ -0,0 +1,145 @@
// Helper function to make AJAX requests
function makeRequest(url, method, data) {
console.log('Making request to:', url, 'with data:', data);
return fetch(url, {
method: method,
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: new URLSearchParams(data)
})
.then(response => {
console.log('Response status:', response.status);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return response.json();
})
.then(data => {
console.log('Response data:', data);
return data;
})
.catch(error => {
console.error('Request error:', error);
throw error;
});
}
// Helper function to show toast messages
function showToast(message, type = 'success') {
const toast = document.createElement('div');
toast.className = `toast toast-${type}`;
toast.textContent = message;
document.body.appendChild(toast);
setTimeout(() => {
toast.classList.add('show');
}, 100);
setTimeout(() => {
toast.classList.remove('show');
setTimeout(() => {
document.body.removeChild(toast);
}, 300);
}, 3000);
}
// Initialize add to cart functionality
function initAddToCart() {
document.querySelectorAll('.add-to-cart-form').forEach(form => {
form.addEventListener('submit', function(e) {
e.preventDefault();
const productId = this.querySelector('input[name="product_id"]').value;
const quantity = this.querySelector('input[name="quantity"]').value;
makeRequest('/unimarket/ajax/add_to_cart.php', 'POST', {
product_id: productId,
quantity: quantity
})
.then(response => {
if (response.success) {
// Update cart count in header
const cartCount = document.querySelector('.cart-count');
if (cartCount) {
cartCount.textContent = response.cart_count;
cartCount.classList.add('pulse');
setTimeout(() => {
cartCount.classList.remove('pulse');
}, 500);
}
// Show success message
showToast(response.message);
} else {
showToast(response.message, 'error');
}
})
.catch(error => {
showToast('An error occurred. Please try again.', 'error');
});
});
});
}
// Initialize quantity controls
function initQuantityControls() {
document.querySelectorAll('.quantity-control').forEach(control => {
const input = control.querySelector('.quantity-input');
const increment = control.querySelector('.quantity-increment');
const decrement = control.querySelector('.quantity-decrement');
// Remove any existing event listeners
const newIncrement = increment.cloneNode(true);
const newDecrement = decrement.cloneNode(true);
increment.parentNode.replaceChild(newIncrement, increment);
decrement.parentNode.replaceChild(newDecrement, decrement);
// Add new event listeners
newIncrement.addEventListener('click', function(e) {
e.preventDefault();
e.stopPropagation();
const max = parseInt(input.getAttribute('max'));
const current = parseInt(input.value) || 1;
if (current < max) {
input.value = current + 1;
} else {
showToast(`Only ${max} available in stock`, 'error');
}
});
newDecrement.addEventListener('click', function(e) {
e.preventDefault();
e.stopPropagation();
const current = parseInt(input.value) || 1;
if (current > 1) {
input.value = current - 1;
}
});
// Handle direct input
input.addEventListener('input', function() {
const max = parseInt(this.getAttribute('max'));
let value = parseInt(this.value);
if (isNaN(value) || value < 1) {
value = 1;
} else if (value > max) {
value = max;
showToast(`Only ${max} available in stock`, 'error');
}
this.value = value;
});
});
}
// Initialize when DOM is loaded
document.addEventListener('DOMContentLoaded', function() {
console.log('DOM loaded, initializing cart functionality');
initAddToCart();
initQuantityControls();
});
+45
View File
@@ -0,0 +1,45 @@
// Create toast container if it doesn't exist
function ensureToastContainer() {
let container = document.querySelector('.toast-container');
if (!container) {
container = document.createElement('div');
container.className = 'toast-container';
document.body.appendChild(container);
}
return container;
}
// Show a toast notification
function showToast(message, type = 'success') {
const container = ensureToastContainer();
// Create toast element
const toast = document.createElement('div');
toast.className = `toast ${type}`;
// Add icon based on type
const icon = document.createElement('i');
icon.className = type === 'success' ? 'fas fa-check-circle' : 'fas fa-exclamation-circle';
toast.appendChild(icon);
// Add message
const messageText = document.createElement('span');
messageText.textContent = message;
toast.appendChild(messageText);
// Add to container
container.appendChild(toast);
// Trigger animation
setTimeout(() => {
toast.classList.add('show');
}, 10);
// Remove after 3 seconds
setTimeout(() => {
toast.classList.remove('show');
setTimeout(() => {
container.removeChild(toast);
}, 300);
}, 3000);
}
+425
View File
@@ -0,0 +1,425 @@
/**
* UniMarket - Main JavaScript File
* Handles frontend interactions, form validations, and UI enhancements
*/
document.addEventListener('DOMContentLoaded', function() {
// Mobile menu toggle functionality
const mobileMenuToggle = document.querySelector('.mobile-menu-toggle');
if (mobileMenuToggle) {
mobileMenuToggle.addEventListener('click', function() {
const menu = document.getElementById('mobileMenu');
if (menu) {
menu.style.display = menu.style.display === 'block' ? 'none' : 'block';
}
});
}
// Initialize all tooltips
initTooltips();
// Initialize all form validations
initFormValidations();
// Initialize product quantity controls
initQuantityControls();
// Initialize image preview for file uploads
initImagePreviews();
// Initialize date/time pickers
initDateTimePickers();
// Initialize cart interactions
if (document.querySelector('.cart-page')) {
initCartInteractions();
}
});
/**
* Initialize tooltips throughout the application
*/
function initTooltips() {
const tooltips = document.querySelectorAll('[data-tooltip]');
tooltips.forEach(tooltip => {
const tooltipText = tooltip.getAttribute('data-tooltip');
const tooltipElement = document.createElement('span');
tooltipElement.className = 'tooltip-text';
tooltipElement.textContent = tooltipText;
tooltip.appendChild(tooltipElement);
// Position tooltip on hover
tooltip.addEventListener('mouseenter', function() {
const rect = this.getBoundingClientRect();
tooltipElement.style.left = `${rect.width / 2}px`;
tooltipElement.style.top = `${rect.height + 5}px`;
tooltipElement.style.opacity = '1';
});
tooltip.addEventListener('mouseleave', function() {
tooltipElement.style.opacity = '0';
});
});
}
/**
* Initialize form validation handlers
*/
function initFormValidations() {
const forms = document.querySelectorAll('form[data-validate]');
forms.forEach(form => {
form.addEventListener('submit', function(e) {
let isValid = true;
const requiredFields = this.querySelectorAll('[required]');
requiredFields.forEach(field => {
if (!field.value.trim()) {
isValid = false;
field.classList.add('error');
// Create or show error message
let errorMsg = field.nextElementSibling;
if (!errorMsg || !errorMsg.classList.contains('error-message')) {
errorMsg = document.createElement('small');
errorMsg.className = 'error-message';
errorMsg.textContent = 'This field is required';
field.parentNode.insertBefore(errorMsg, field.nextSibling);
}
} else {
field.classList.remove('error');
const errorMsg = field.nextElementSibling;
if (errorMsg && errorMsg.classList.contains('error-message')) {
errorMsg.remove();
}
}
});
if (!isValid) {
e.preventDefault();
// Scroll to first error
const firstError = this.querySelector('.error');
if (firstError) {
firstError.scrollIntoView({ behavior: 'smooth', block: 'center' });
}
}
});
});
}
/**
* Initialize quantity controls for product pages
*/
function initQuantityControls() {
document.querySelectorAll('.quantity-control').forEach(control => {
const input = control.querySelector('.quantity-input');
const increment = control.querySelector('.quantity-increment');
const decrement = control.querySelector('.quantity-decrement');
increment.addEventListener('click', () => {
input.value = parseInt(input.value) + 1;
input.dispatchEvent(new Event('change'));
});
decrement.addEventListener('click', () => {
if (parseInt(input.value) > 1) {
input.value = parseInt(input.value) - 1;
input.dispatchEvent(new Event('change'));
}
});
// Validate input
input.addEventListener('change', () => {
if (parseInt(input.value) < 1) {
input.value = 1;
}
});
});
}
/**
* Initialize image previews for file uploads
*/
function initImagePreviews() {
document.querySelectorAll('.image-upload').forEach(upload => {
const input = upload.querySelector('input[type="file"]');
const preview = upload.querySelector('.image-preview');
const defaultText = upload.querySelector('.default-text');
if (input && preview) {
input.addEventListener('change', function() {
const file = this.files[0];
if (file) {
const reader = new FileReader();
reader.onload = function(e) {
preview.style.backgroundImage = `url(${e.target.result})`;
preview.classList.add('has-image');
if (defaultText) defaultText.style.display = 'none';
}
reader.readAsDataURL(file);
}
});
}
});
}
/**
* Initialize date/time pickers for pickup scheduling
*/
function initDateTimePickers() {
const datePickers = document.querySelectorAll('.date-picker');
if (datePickers.length > 0) {
// Load flatpickr if not already loaded
if (typeof flatpickr === 'undefined') {
const script = document.createElement('script');
script.src = 'https://cdn.jsdelivr.net/npm/flatpickr';
script.onload = function() {
setupDateTimePickers();
};
document.head.appendChild(script);
const style = document.createElement('link');
style.rel = 'stylesheet';
style.href = 'https://cdn.jsdelivr.net/npm/flatpickr/dist/flatpickr.min.css';
document.head.appendChild(style);
} else {
setupDateTimePickers();
}
}
function setupDateTimePickers() {
document.querySelectorAll('.date-picker').forEach(picker => {
flatpickr(picker, {
enableTime: picker.classList.contains('time-picker'),
dateFormat: picker.classList.contains('time-picker') ? "Y-m-d H:i" : "Y-m-d",
minDate: "today",
time_24hr: true
});
});
}
}
/**
* Initialize cart interactions
*/
function initCartInteractions() {
// Update cart item quantities
document.querySelectorAll('.cart-item-quantity').forEach(input => {
input.addEventListener('change', function() {
const form = this.closest('form');
if (form) {
form.submit();
}
});
});
// Handle remove item buttons
document.querySelectorAll('.remove-item-btn').forEach(button => {
button.addEventListener('click', function(e) {
if (!confirm('Are you sure you want to remove this item from your cart?')) {
e.preventDefault();
}
});
});
// Handle checkout button
const checkoutBtn = document.querySelector('.checkout-btn');
if (checkoutBtn) {
checkoutBtn.addEventListener('click', function(e) {
if (document.querySelector('.cart-item').length === 0) {
e.preventDefault();
alert('Your cart is empty. Please add items before checking out.');
}
});
}
}
/**
* AJAX helper function
*/
function makeRequest(url, method = 'GET', data = null) {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.open(method, url);
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
xhr.onload = function() {
if (xhr.status >= 200 && xhr.status < 300) {
try {
resolve(JSON.parse(xhr.responseText));
} catch (e) {
resolve(xhr.responseText);
}
} else {
reject(xhr.statusText);
}
};
xhr.onerror = function() {
reject(xhr.statusText);
};
xhr.send(data ? JSON.stringify(data) : null);
});
}
/**
* Display flash messages
*/
function displayFlashMessages() {
const flashMessages = document.querySelector('.flash-messages');
if (flashMessages) {
setTimeout(() => {
flashMessages.style.opacity = '0';
setTimeout(() => {
flashMessages.remove();
}, 500);
}, 5000);
}
}
// Display flash messages when page loads
displayFlashMessages();
/**
* Debounce function for performance optimization
*/
function debounce(func, wait, immediate) {
let timeout;
return function() {
const context = this, args = arguments;
const later = function() {
timeout = null;
if (!immediate) func.apply(context, args);
};
const callNow = immediate && !timeout;
clearTimeout(timeout);
timeout = setTimeout(later, wait);
if (callNow) func.apply(context, args);
};
}
/**
* Throttle function for performance optimization
*/
function throttle(func, limit) {
let lastFunc;
let lastRan;
return function() {
const context = this;
const args = arguments;
if (!lastRan) {
func.apply(context, args);
lastRan = Date.now();
} else {
clearTimeout(lastFunc);
lastFunc = setTimeout(function() {
if ((Date.now() - lastRan) >= limit) {
func.apply(context, args);
lastRan = Date.now();
}
}, limit - (Date.now() - lastRan));
}
}
}
// Add to cart functionality
document.querySelectorAll('.add-to-cart').forEach(button => {
button.addEventListener('click', function(e) {
e.preventDefault();
const productId = this.dataset.productId;
const quantity = this.closest('form')?.querySelector('input[name="quantity"]')?.value || 1;
makeRequest('/unimarket/ajax/add_to_cart.php', 'POST', {
product_id: productId,
quantity: quantity
})
.then(response => {
if (response.success) {
// Update cart count in header
const cartCount = document.querySelector('.cart-count');
if (cartCount) {
cartCount.textContent = response.cart_count;
cartCount.classList.add('pulse');
setTimeout(() => {
cartCount.classList.remove('pulse');
}, 500);
}
// Show success message
showToast('Item added to cart!');
} else {
showToast(response.message || 'Failed to add item to cart', 'error');
}
})
.catch(error => {
showToast('An error occurred. Please try again.', 'error');
});
});
});
/**
* Show toast notification
*/
function showToast(message, type = 'success') {
const toast = document.createElement('div');
toast.className = `toast toast-${type}`;
toast.textContent = message;
document.body.appendChild(toast);
setTimeout(() => {
toast.classList.add('show');
}, 10);
setTimeout(() => {
toast.classList.remove('show');
setTimeout(() => {
toast.remove();
}, 300);
}, 3000);
}
// Add CSS for toast notifications
const toastStyles = document.createElement('style');
toastStyles.textContent = `
.toast {
position: fixed;
bottom: 20px;
right: 20px;
padding: 15px 25px;
border-radius: 4px;
color: white;
background-color: #2e7d32;
box-shadow: 0 3px 10px rgba(0,0,0,0.2);
transform: translateY(100px);
opacity: 0;
transition: all 0.3s ease;
z-index: 1000;
}
.toast.show {
transform: translateY(0);
opacity: 1;
}
.toast-error {
background-color: #d32f2f;
}
.toast-success {
background-color: #2e7d32;
}
.pulse {
animation: pulse 0.5s;
}
@keyframes pulse {
0% { transform: scale(1); }
50% { transform: scale(1.2); }
100% { transform: scale(1); }
}
`;
document.head.appendChild(toastStyles);
+85
View File
@@ -0,0 +1,85 @@
<?php
require_once '../includes/auth_functions.php';
if (isLoggedIn()) {
header("Location: /unimarket/index.php");
exit();
}
$error = '';
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
require_once '../config/database.php';
$username = trim($_POST['username']);
$password = trim($_POST['password']);
if (empty($username) || empty($password)) {
$error = 'Please fill in all fields.';
} else {
$stmt = $pdo->prepare("SELECT * FROM users WHERE username = ?");
$stmt->execute([$username]);
$user = $stmt->fetch();
if ($user && password_verify($password, $user['password'])) {
$_SESSION['user_id'] = $user['user_id'];
$_SESSION['username'] = $user['username'];
$_SESSION['role'] = $user['role'];
if ($user['role'] === 'admin') {
header("Location: /unimarket/admin/dashboard.php");
} elseif ($user['role'] === 'owner') {
header("Location: /unimarket/owner/dashboard.php");
} else {
header("Location: /unimarket/index.php");
}
exit();
} else {
$error = 'Invalid username or password.';
}
}
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Login - UniMarket</title>
<link rel="stylesheet" href="/unimarket/assets/css/style.css">
<link rel="stylesheet" href="/unimarket/assets/css/responsive.css">
</head>
<body>
<?php include '../includes/header.php'; ?>
<div class="container">
<div class="card" style="max-width: 500px; margin: 50px auto;">
<h1 style="text-align: center; margin-bottom: 20px;">Login to UniMarket</h1>
<?php if ($error): ?>
<div class="alert alert-error"><?php echo $error; ?></div>
<?php endif; ?>
<form action="" method="POST">
<div class="form-group">
<label for="username">Username</label>
<input type="text" id="username" name="username" required>
</div>
<div class="form-group">
<label for="password">Password</label>
<input type="password" id="password" name="password" required>
</div>
<button type="submit" class="btn">Login</button>
</form>
<p style="text-align: center; margin-top: 20px;">
Don't have an account? <a href="/unimarket/auth/register.php">Register here</a>
</p>
</div>
</div>
<?php include '../includes/footer.php'; ?>
</body>
</html>
+12
View File
@@ -0,0 +1,12 @@
<?php
require_once __DIR__ . '/../includes/auth_functions.php';
// Destroy the current session
destroySession();
// Set a success flash message
flashMessage('logout_success', 'You have been successfully logged out.');
// Redirect to the homepage with a success message
header("Location: /unimarket/index.php");
exit();
+325
View File
@@ -0,0 +1,325 @@
<?php
require_once '../includes/auth_functions.php';
if (isLoggedIn()) {
header("Location: /unimarket/index.php");
exit();
}
require_once '../config/database.php';
$errors = [];
$success = false;
// Form fields
$fields = [
'username' => '',
'email' => '',
'first_name' => '',
'last_name' => '',
'university_id' => '',
'phone' => '',
'role' => 'customer' // Default role
];
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
// Sanitize and validate input
$fields['username'] = trim($_POST['username']);
$fields['email'] = trim($_POST['email']);
$fields['first_name'] = trim($_POST['first_name']);
$fields['last_name'] = trim($_POST['last_name']);
$fields['university_id'] = trim($_POST['university_id']);
$fields['phone'] = trim($_POST['phone']);
$fields['role'] = $_POST['role'] === 'owner' ? 'owner' : 'customer';
$password = trim($_POST['password']);
$confirm_password = trim($_POST['confirm_password']);
// Validate inputs
if (empty($fields['username'])) {
$errors['username'] = 'Username is required.';
} elseif (!preg_match('/^[a-zA-Z0-9_]{4,20}$/', $fields['username'])) {
$errors['username'] = 'Username must be 4-20 characters (letters, numbers, underscores).';
}
if (empty($fields['email'])) {
$errors['email'] = 'Email is required.';
} elseif (!filter_var($fields['email'], FILTER_VALIDATE_EMAIL)) {
$errors['email'] = 'Please enter a valid email address.';
}
if (empty($fields['first_name'])) {
$errors['first_name'] = 'First name is required.';
}
if (empty($fields['last_name'])) {
$errors['last_name'] = 'Last name is required.';
}
if (empty($fields['university_id'])) {
$errors['university_id'] = 'University ID is required.';
}
if (empty($password)) {
$errors['password'] = 'Password is required.';
} elseif (strlen($password) < 8) {
$errors['password'] = 'Password must be at least 8 characters.';
} elseif ($password !== $confirm_password) {
$errors['confirm_password'] = 'Passwords do not match.';
}
// Check if username or email already exists
if (empty($errors)) {
$stmt = $pdo->prepare("SELECT user_id FROM users WHERE username = ? OR email = ?");
$stmt->execute([$fields['username'], $fields['email']]);
if ($stmt->fetch()) {
$errors['general'] = 'Username or email already exists.';
}
}
// If no errors, create user
if (empty($errors)) {
try {
$hashed_password = password_hash($password, PASSWORD_DEFAULT);
$stmt = $pdo->prepare("INSERT INTO users
(username, password, email, first_name, last_name, university_id, phone, role)
VALUES (?, ?, ?, ?, ?, ?, ?, ?)");
$stmt->execute([
$fields['username'],
$hashed_password,
$fields['email'],
$fields['first_name'],
$fields['last_name'],
$fields['university_id'],
$fields['phone'],
$fields['role']
]);
// Create a cart for customers
if ($fields['role'] === 'customer') {
$user_id = $pdo->lastInsertId();
$stmt = $pdo->prepare("INSERT INTO cart (user_id) VALUES (?)");
$stmt->execute([$user_id]);
}
$success = true;
} catch (PDOException $e) {
$errors['general'] = 'Registration failed. Please try again.';
}
}
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Register - UniMarket</title>
<link rel="stylesheet" href="/unimarket/assets/css/style.css">
<link rel="stylesheet" href="/unimarket/assets/css/responsive.css">
<style>
.role-selector {
display: flex;
margin-bottom: 20px;
border: 1px solid #ddd;
border-radius: 4px;
overflow: hidden;
}
.role-option {
flex: 1;
text-align: center;
padding: 10px;
cursor: pointer;
transition: background-color 0.3s;
}
.role-option input {
display: none;
}
.role-option label {
display: block;
cursor: pointer;
}
.role-option:hover {
background-color: #f5f5f5;
}
.role-option.active {
background-color: var(--primary-color);
color: white;
}
.password-strength {
height: 5px;
background-color: #eee;
margin-top: 5px;
border-radius: 3px;
overflow: hidden;
}
.password-strength-bar {
height: 100%;
width: 0%;
background-color: #ff0000;
transition: width 0.3s, background-color 0.3s;
}
</style>
</head>
<body>
<?php include '../includes/header.php'; ?>
<div class="container">
<div class="card" style="max-width: 600px; margin: 50px auto;">
<h1 style="text-align: center; margin-bottom: 20px;">Create Your Account</h1>
<?php if ($success): ?>
<div class="alert alert-success">
Registration successful! You can now <a href="/unimarket/auth/login.php">login</a>.
</div>
<?php else: ?>
<?php if (isset($errors['general'])): ?>
<div class="alert alert-error"><?php echo $errors['general']; ?></div>
<?php endif; ?>
<form action="" method="POST" id="registrationForm">
<div class="role-selector">
<div class="role-option <?php echo $fields['role'] === 'customer' ? 'active' : ''; ?>">
<input type="radio" id="role_customer" name="role" value="customer" <?php echo $fields['role'] === 'customer' ? 'checked' : ''; ?>>
<label for="role_customer">I'm a Student</label>
</div>
<div class="role-option <?php echo $fields['role'] === 'owner' ? 'active' : ''; ?>">
<input type="radio" id="role_owner" name="role" value="owner" <?php echo $fields['role'] === 'owner' ? 'checked' : ''; ?>>
<label for="role_owner">I'm a Vendor</label>
</div>
</div>
<div class="form-group">
<label for="username">Username*</label>
<input type="text" id="username" name="username" value="<?php echo htmlspecialchars($fields['username']); ?>" required>
<?php if (isset($errors['username'])): ?>
<small style="color: var(--error-color);"><?php echo $errors['username']; ?></small>
<?php endif; ?>
</div>
<div class="form-group">
<label for="email">Email*</label>
<input type="email" id="email" name="email" value="<?php echo htmlspecialchars($fields['email']); ?>" required>
<?php if (isset($errors['email'])): ?>
<small style="color: var(--error-color);"><?php echo $errors['email']; ?></small>
<?php endif; ?>
</div>
<div class="form-group">
<label for="first_name">First Name*</label>
<input type="text" id="first_name" name="first_name" value="<?php echo htmlspecialchars($fields['first_name']); ?>" required>
<?php if (isset($errors['first_name'])): ?>
<small style="color: var(--error-color);"><?php echo $errors['first_name']; ?></small>
<?php endif; ?>
</div>
<div class="form-group">
<label for="last_name">Last Name*</label>
<input type="text" id="last_name" name="last_name" value="<?php echo htmlspecialchars($fields['last_name']); ?>" required>
<?php if (isset($errors['last_name'])): ?>
<small style="color: var(--error-color);"><?php echo $errors['last_name']; ?></small>
<?php endif; ?>
</div>
<div class="form-group">
<label for="university_id">University ID*</label>
<input type="text" id="university_id" name="university_id" value="<?php echo htmlspecialchars($fields['university_id']); ?>" required>
<?php if (isset($errors['university_id'])): ?>
<small style="color: var(--error-color);"><?php echo $errors['university_id']; ?></small>
<?php endif; ?>
</div>
<div class="form-group">
<label for="phone">Phone Number</label>
<input type="tel" id="phone" name="phone" class="form-control" value="<?php echo htmlspecialchars($fields['phone']); ?>">
</div>
<div class="form-group">
<label for="password">Password*</label>
<input type="password" id="password" name="password" required>
<div class="password-strength">
<div class="password-strength-bar" id="passwordStrengthBar"></div>
</div>
<?php if (isset($errors['password'])): ?>
<small style="color: var(--error-color);"><?php echo $errors['password']; ?></small>
<?php endif; ?>
</div>
<div class="form-group">
<label for="confirm_password">Confirm Password*</label>
<input type="password" id="confirm_password" name="confirm_password" required>
<?php if (isset($errors['confirm_password'])): ?>
<small style="color: var(--error-color);"><?php echo $errors['confirm_password']; ?></small>
<?php endif; ?>
</div>
<button type="submit" class="btn" style="width: 100%;">Register</button>
<p style="text-align: center; margin-top: 20px;">
Already have an account? <a href="/unimarket/auth/login.php">Login here</a>
</p>
</form>
<?php endif; ?>
</div>
</div>
<?php include '../includes/footer.php'; ?>
<script>
// Role selector styling
document.querySelectorAll('.role-option').forEach(option => {
option.addEventListener('click', function() {
document.querySelectorAll('.role-option').forEach(opt => {
opt.classList.remove('active');
});
this.classList.add('active');
this.querySelector('input').checked = true;
});
});
// Password strength indicator
const passwordInput = document.getElementById('password');
const strengthBar = document.getElementById('passwordStrengthBar');
passwordInput.addEventListener('input', function() {
const password = this.value;
let strength = 0;
if (password.length >= 8) strength += 1;
if (password.match(/[a-z]/)) strength += 1;
if (password.match(/[A-Z]/)) strength += 1;
if (password.match(/[0-9]/)) strength += 1;
if (password.match(/[^a-zA-Z0-9]/)) strength += 1;
const width = strength * 20;
let color;
if (strength <= 1) color = '#ff0000';
else if (strength <= 3) color = '#ff9900';
else color = '#00aa00';
strengthBar.style.width = width + '%';
strengthBar.style.backgroundColor = color;
});
// Form validation
document.getElementById('registrationForm').addEventListener('submit', function(e) {
const password = document.getElementById('password').value;
const confirmPassword = document.getElementById('confirm_password').value;
if (password !== confirmPassword) {
e.preventDefault();
alert('Passwords do not match!');
}
});
</script>
</body>
</html>
+13
View File
@@ -0,0 +1,13 @@
<?php
$host = 'localhost';
$dbname = 'unimarket';
$username = 'root';
$password = '';
try {
$pdo = new PDO("mysql:host=$host;dbname=$dbname", $username, $password);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch (PDOException $e) {
die("Database connection failed: " . $e->getMessage());
}
?>
+14
View File
@@ -0,0 +1,14 @@
CREATE TABLE IF NOT EXISTS products (
product_id INT PRIMARY KEY AUTO_INCREMENT,
owner_id INT NOT NULL,
name VARCHAR(255) NOT NULL,
description TEXT,
price DECIMAL(10,2) NOT NULL,
category VARCHAR(50),
subcategory VARCHAR(50),
stock_quantity INT NOT NULL DEFAULT 0,
image_path VARCHAR(255),
size_data JSON,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (owner_id) REFERENCES users(user_id)
);
+688
View File
@@ -0,0 +1,688 @@
<?php
require_once '../includes/auth_functions.php';
redirectIfNotLoggedIn();
redirectIfNotCustomer();
require_once '../config/database.php';
// Initialize variables
$success_message = '';
$cart_items = [];
// Get cart items
$stmt = $pdo->prepare("
SELECT
ci.cart_item_id,
ci.product_id,
ci.quantity,
ci.size,
p.name as product_name,
p.price,
p.image_path,
p.stock_quantity,
p.category,
p.size_data
FROM cart_items ci
JOIN products p ON ci.product_id = p.product_id
JOIN cart c ON ci.cart_id = c.cart_id
WHERE c.user_id = ?
");
$stmt->execute([$_SESSION['user_id']]);
$cart_items = $stmt->fetchAll();
// Calculate total
$total = array_reduce($cart_items, function($sum, $item) {
return $sum + ($item['price'] * $item['quantity']);
}, 0);
// Handle checkout process
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['checkout'])) {
try {
// Start transaction
$pdo->beginTransaction();
// Get cart items
$stmt = $pdo->prepare("
SELECT
ci.cart_item_id,
ci.product_id,
ci.quantity,
ci.size,
p.price,
p.stock_quantity,
p.owner_id,
p.name as product_name,
p.category,
p.size_data
FROM cart_items ci
JOIN products p ON ci.product_id = p.product_id
JOIN cart c ON ci.cart_id = c.cart_id
WHERE c.user_id = ?
");
$stmt->execute([$_SESSION['user_id']]);
$cart_items = $stmt->fetchAll();
// Debug cart items
error_log("Cart items before order creation: " . print_r($cart_items, true));
if (empty($cart_items)) {
throw new Exception('Your cart is empty');
}
// Create order
$stmt = $pdo->prepare("
INSERT INTO orders (customer_id, total_amount, status, order_date)
VALUES (?, ?, 'pending', NOW())
");
$total_amount = array_reduce($cart_items, function($sum, $item) {
return $sum + ($item['price'] * $item['quantity']);
}, 0);
$stmt->execute([$_SESSION['user_id'], $total_amount]);
$order_id = $pdo->lastInsertId();
// Create order items
$stmt = $pdo->prepare("
INSERT INTO order_items (order_id, product_id, quantity, price, size)
VALUES (?, ?, ?, ?, ?)
");
foreach ($cart_items as $item) {
// Debug each item being added to order
error_log("Adding item to order: " . print_r($item, true));
// Check stock
if ($item['quantity'] > $item['stock_quantity']) {
throw new Exception("Not enough stock for {$item['product_name']}");
}
// For clothing items, ensure size is set
$size = null;
if ($item['category'] === 'clothes') {
$size = $item['size'];
if (empty($size)) {
throw new Exception("Size is required for clothing item: {$item['product_name']}");
}
}
// Add order item
$stmt->execute([
$order_id,
$item['product_id'],
$item['quantity'],
$item['price'],
$size
]);
// Update product stock
$update_stock = $pdo->prepare("
UPDATE products
SET stock_quantity = stock_quantity - ?
WHERE product_id = ?
");
$update_stock->execute([$item['quantity'], $item['product_id']]);
}
// Clear cart
$stmt = $pdo->prepare("
DELETE ci FROM cart_items ci
JOIN cart c ON ci.cart_id = c.cart_id
WHERE c.user_id = ?
");
$stmt->execute([$_SESSION['user_id']]);
// Commit transaction
$pdo->commit();
// Set success message
$success_message = 'Order placed successfully! Your order #' . $order_id . ' is being processed.';
// Clear cart items array after successful checkout
$cart_items = [];
} catch (Exception $e) {
// Rollback transaction on error
$pdo->rollBack();
$success_message = $e->getMessage();
}
} else {
// Get cart items only if not processing checkout
$stmt = $pdo->prepare("
SELECT
ci.cart_item_id,
ci.product_id,
ci.quantity,
ci.size,
p.name as product_name,
p.price,
p.image_path,
p.stock_quantity,
p.category,
p.size_data
FROM cart_items ci
JOIN products p ON ci.product_id = p.product_id
JOIN cart c ON ci.cart_id = c.cart_id
WHERE c.user_id = ?
");
$stmt->execute([$_SESSION['user_id']]);
$cart_items = $stmt->fetchAll();
}
// Calculate total
$total = array_reduce($cart_items, function($sum, $item) {
return $sum + ($item['price'] * $item['quantity']);
}, 0);
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Shopping Cart - UniMarket</title>
<link rel="stylesheet" href="/unimarket/assets/css/style.css">
<link rel="stylesheet" href="/unimarket/assets/css/responsive.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
<script src="/unimarket/assets/js/notifications.js"></script>
<style>
/* Cart item styles */
.cart-container {
display: flex;
gap: 20px;
margin-top: 20px;
}
.cart-items {
flex: 1;
}
.cart-item {
display: flex;
gap: 20px;
padding: 15px;
margin-bottom: 15px;
}
.item-image {
width: 120px;
height: 120px;
flex-shrink: 0;
background: #f8f9fa;
border-radius: 4px;
overflow: hidden;
display: flex;
align-items: center;
justify-content: center;
}
.item-image img {
width: 100%;
height: 100%;
object-fit: cover;
}
.item-image .no-image {
color: #ccc;
font-size: 2em;
}
.item-details {
flex: 1;
display: flex;
flex-direction: column;
gap: 10px;
}
.item-details h3 {
margin: 0;
font-size: 1.1em;
}
.item-price {
font-weight: 500;
color: var(--primary-color);
}
.item-size {
color: #666;
font-size: 0.9em;
margin: 5px 0;
}
.quantity-controls {
display: flex;
align-items: center;
gap: 10px;
}
.quantity-controls button {
width: 30px;
height: 30px;
border: 1px solid #ddd;
background: white;
border-radius: 4px;
cursor: pointer;
}
.quantity-input {
width: 60px;
text-align: center;
padding: 5px;
border: 1px solid #ddd;
border-radius: 4px;
}
.remove-item {
color: #dc3545;
background: none;
border: none;
cursor: pointer;
padding: 5px 0;
display: flex;
align-items: center;
gap: 5px;
}
.remove-item:hover {
color: #c82333;
}
.cart-summary {
width: 300px;
padding: 20px;
}
.cart-total {
display: flex;
justify-content: space-between;
align-items: center;
font-size: 1.2em;
font-weight: 500;
margin: 20px 0;
}
.cart-actions {
display: flex;
flex-direction: column;
gap: 10px;
}
.empty-cart {
text-align: center;
padding: 50px;
background: #f8f9fa;
border-radius: 8px;
}
.empty-cart i {
font-size: 3em;
color: #ccc;
margin-bottom: 20px;
}
/* Purchase confirmation modal styles */
.purchase-modal {
display: none;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.5);
z-index: 2000;
justify-content: center;
align-items: center;
}
.purchase-modal-content {
background: white;
padding: 30px;
border-radius: 8px;
text-align: center;
max-width: 400px;
width: 90%;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
}
.purchase-modal h2 {
margin-bottom: 20px;
color: var(--primary-dark);
}
.purchase-modal-buttons {
display: flex;
justify-content: center;
gap: 15px;
margin-top: 25px;
}
.purchase-modal-buttons button {
padding: 10px 25px;
border: none;
border-radius: 4px;
cursor: pointer;
font-weight: 500;
transition: all 0.3s ease;
}
.purchase-modal-buttons .btn-cancel {
background: #e0e0e0;
color: #333;
}
.purchase-modal-buttons .btn-cancel:hover {
background: #d0d0d0;
}
.purchase-modal-buttons .btn-confirm {
background: var(--primary-color);
color: white;
}
.purchase-modal-buttons .btn-confirm:hover {
background: var(--primary-dark);
}
.payment-info {
background: #f8f9fa;
padding: 15px;
border-radius: 4px;
margin: 20px 0;
text-align: left;
}
.payment-info i {
color: var(--primary-color);
margin-right: 10px;
}
.payment-info p {
margin: 0;
color: #666;
font-size: 0.9rem;
}
@media (max-width: 768px) {
.cart-container {
flex-direction: column;
}
.cart-summary {
width: 100%;
}
.cart-item {
flex-direction: column;
}
.item-image {
width: 100%;
height: 200px;
}
}
.notification {
position: fixed;
top: 20px;
right: 20px;
padding: 15px 25px;
border-radius: 5px;
color: white;
font-weight: 500;
z-index: 1000;
animation: slideIn 0.5s ease-out;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
background-color: #28a745;
}
@keyframes slideIn {
from {
transform: translateX(100%);
opacity: 0;
}
to {
transform: translateX(0);
opacity: 1;
}
}
@keyframes slideOut {
from {
transform: translateX(0);
opacity: 1;
}
to {
transform: translateX(100%);
opacity: 0;
}
}
.notification.hide {
animation: slideOut 0.5s ease-out forwards;
}
</style>
</head>
<body>
<?php include '../includes/header.php'; ?>
<div class="container">
<h1>Shopping Cart</h1>
<?php if (empty($cart_items)): ?>
<div class="empty-cart">
<i class="fas fa-shopping-cart"></i>
<h2>Your cart is empty</h2>
<p>Looks like you haven't added any items to your cart yet.</p>
<a href="/unimarket/customer/products/browse.php" class="btn">Continue Shopping</a>
</div>
<?php else: ?>
<div class="cart-container">
<div class="cart-items">
<?php foreach ($cart_items as $item): ?>
<div class="cart-item card">
<div class="item-image">
<?php if ($item['image_path']): ?>
<img src="/unimarket/assets/images/products/<?php echo $item['image_path']; ?>"
alt="<?php echo htmlspecialchars($item['product_name']); ?>">
<?php else: ?>
<div class="no-image">
<i class="fas fa-box-open"></i>
</div>
<?php endif; ?>
</div>
<div class="item-details">
<h3><?php echo htmlspecialchars($item['product_name']); ?></h3>
<p class="item-price">₱<?php echo number_format($item['price'], 2); ?></p>
<?php if ($item['category'] === 'clothes' && isset($item['size'])): ?>
<p class="item-size">Size: <?php echo htmlspecialchars($item['size']); ?></p>
<?php endif; ?>
<form method="POST" action="../ajax/update_cart.php" class="quantity-form">
<input type="hidden" name="cart_item_id" value="<?php echo $item['cart_item_id']; ?>">
<div class="quantity-controls">
<button type="button" class="quantity-decrement">-</button>
<input type="number" name="quantity" value="<?php echo $item['quantity']; ?>"
min="1" max="<?php echo $item['stock_quantity']; ?>"
class="quantity-input">
<button type="button" class="quantity-increment">+</button>
</div>
</form>
<p class="item-total">₱<?php echo number_format($item['price'] * $item['quantity'], 2); ?></p>
<form method="POST" action="../ajax/remove_from_cart.php" class="remove-form">
<input type="hidden" name="cart_item_id" value="<?php echo $item['cart_item_id']; ?>">
<button type="button" class="remove-item">
<i class="fas fa-trash"></i> Remove
</button>
</form>
</div>
</div>
<?php endforeach; ?>
</div>
<div class="cart-summary card">
<h2>Order Summary</h2>
<div class="cart-total">
<span>Total:</span>
<span class="total-amount">₱<?php echo number_format($total, 2); ?></span>
</div>
<div class="cart-actions">
<a href="/unimarket/customer/products/browse.php" class="btn btn-secondary">
<i class="fas fa-arrow-left"></i> Continue Shopping
</a>
<form method="POST" id="checkoutForm">
<input type="hidden" name="checkout" value="1">
<button type="button" onclick="openPurchaseModal()" class="btn btn-primary">
<i class="fas fa-shopping-bag"></i> Proceed to Checkout
</button>
</form>
</div>
<!-- Purchase confirmation modal HTML -->
<div class="purchase-modal" id="purchaseModal">
<div class="purchase-modal-content">
<h2>Confirm Purchase</h2>
<p>Are you sure you want to proceed with this purchase?</p>
<div class="payment-info">
<i class="fas fa-info-circle"></i>
<p>Payment will be collected when you pick up your items. The seller will contact you to schedule the pickup time.</p>
</div>
<div class="purchase-modal-buttons">
<button class="btn-cancel" onclick="closePurchaseModal()">Cancel</button>
<button class="btn-confirm" onclick="confirmPurchase()">Confirm Purchase</button>
</div>
</div>
</div>
</div>
</div>
<?php endif; ?>
</div>
<?php include '../includes/footer.php'; ?>
<script>
// Purchase modal functions
function openPurchaseModal() {
document.getElementById('purchaseModal').style.display = 'flex';
}
function closePurchaseModal() {
document.getElementById('purchaseModal').style.display = 'none';
}
function confirmPurchase() {
document.getElementById('checkoutForm').submit();
}
// Close modal when clicking outside
document.getElementById('purchaseModal').addEventListener('click', function(e) {
if (e.target === this) {
closePurchaseModal();
}
});
// Quantity update handling
document.querySelectorAll('.quantity-input').forEach(input => {
input.addEventListener('change', function() {
const form = this.closest('form');
const formData = new FormData(form);
fetch('../ajax/update_cart.php', {
method: 'POST',
body: formData
})
.then(response => response.json())
.then(data => {
if (data.success) {
// Update total price
const totalElement = document.querySelector('.cart-total .total-amount');
if (totalElement) {
totalElement.textContent = '₱' + data.total.toFixed(2);
}
// Update item total
const itemTotal = this.closest('.cart-item').querySelector('.item-total');
if (itemTotal) {
itemTotal.textContent = '₱' + data.item_total.toFixed(2);
}
} else {
alert(data.message || 'Failed to update quantity');
// Reset to previous value
this.value = this.defaultValue;
}
})
.catch(error => {
console.error('Error:', error);
alert('An error occurred while updating quantity');
// Reset to previous value
this.value = this.defaultValue;
});
});
});
// Remove item handling
document.querySelectorAll('.remove-item').forEach(button => {
button.addEventListener('click', function() {
if (confirm('Are you sure you want to remove this item from your cart?')) {
const form = this.closest('form');
const formData = new FormData(form);
fetch('../ajax/remove_from_cart.php', {
method: 'POST',
body: formData
})
.then(response => response.json())
.then(data => {
if (data.success) {
// Remove item from DOM
const cartItem = this.closest('.cart-item');
cartItem.remove();
// Update total price
const totalElement = document.querySelector('.cart-total .total-amount');
if (totalElement) {
totalElement.textContent = '₱' + data.total.toFixed(2);
}
// Update cart count in header
const cartCount = document.querySelector('.cart-count');
if (cartCount) {
cartCount.textContent = data.cart_count;
}
// Show success message
showToast('Item removed from cart', 'success');
// If cart is empty, reload page to show empty cart message
if (data.cart_count === 0) {
location.reload();
}
} else {
showToast(data.message || 'Failed to remove item', 'error');
}
})
.catch(error => {
console.error('Error:', error);
showToast('An error occurred while removing item', 'error');
});
}
});
});
// Auto-hide notifications after 5 seconds
document.addEventListener('DOMContentLoaded', function() {
const notifications = document.querySelectorAll('.notification');
notifications.forEach(notification => {
setTimeout(() => {
notification.classList.add('hide');
setTimeout(() => {
notification.remove();
}, 500);
}, 5000);
});
});
</script>
</body>
</html>
+91
View File
@@ -0,0 +1,91 @@
<?php
require_once '../includes/auth_functions.php';
require_once '../config/database.php';
// Check if user is logged in and is a customer
if (!isLoggedIn() || !isCustomer()) {
header('Content-Type: application/json');
echo json_encode(['error' => 'Unauthorized']);
exit();
}
// Get user ID
$user_id = $_SESSION['user_id'];
try {
// Get notifications for the user's orders
$stmt = $pdo->prepare("
SELECT
o.order_id,
o.status,
o.order_date,
CASE
WHEN o.status = 'ready' THEN CONCAT('Your order #', o.order_id, ' is ready for pickup!')
WHEN o.status = 'completed' THEN CONCAT('Your order #', o.order_id, ' has been completed.')
WHEN o.status = 'cancelled' THEN CONCAT('Your order #', o.order_id, ' has been cancelled.')
ELSE NULL
END as title,
o.order_date as notification_time,
CASE
WHEN o.status = 'ready' AND o.notification_read = 0 THEN 1
ELSE 0
END as unread,
o.pickup_schedule
FROM orders o
WHERE o.customer_id = ?
AND o.status IN ('ready', 'completed', 'cancelled')
AND o.order_date >= DATE_SUB(NOW(), INTERVAL 7 DAY)
ORDER BY o.order_date DESC
");
$stmt->execute([$user_id]);
$notifications = $stmt->fetchAll(PDO::FETCH_ASSOC);
// Format notifications
$formatted_notifications = array_map(function($notification) {
$message = $notification['title'];
if ($notification['status'] === 'ready' && $notification['pickup_schedule']) {
$message .= ' Scheduled for ' . date('M d, Y h:i A', strtotime($notification['pickup_schedule']));
}
return [
'id' => $notification['order_id'],
'title' => $message,
'time' => date('M d, Y h:i A', strtotime($notification['notification_time'])),
'unread' => (bool)$notification['unread'],
'status' => $notification['status']
];
}, $notifications);
// Count unread notifications
$unread_count = array_reduce($notifications, function($carry, $notification) {
return $carry + ($notification['unread'] ? 1 : 0);
}, 0);
// Mark notifications as read
if ($unread_count > 0) {
$stmt = $pdo->prepare("
UPDATE orders
SET notification_read = 1
WHERE customer_id = ?
AND status = 'ready'
AND notification_read = 0
");
$stmt->execute([$user_id]);
}
// Return JSON response
header('Content-Type: application/json');
echo json_encode([
'notifications' => $formatted_notifications,
'unread_count' => $unread_count
]);
} catch (PDOException $e) {
error_log("Notification Error: " . $e->getMessage());
header('Content-Type: application/json');
echo json_encode(['error' => 'Database error: ' . $e->getMessage()]);
} catch (Exception $e) {
error_log("Notification Error: " . $e->getMessage());
header('Content-Type: application/json');
echo json_encode(['error' => 'Server error: ' . $e->getMessage()]);
}
+161
View File
@@ -0,0 +1,161 @@
<?php
require_once '../../includes/auth_functions.php';
redirectIfNotLoggedIn();
redirectIfNotCustomer();
require_once '../../config/database.php';
// Check if order ID is provided
if (!isset($_GET['order_id'])) {
header("Location: /unimarket/customer/orders/list.php");
exit();
}
$order_id = (int)$_GET['order_id'];
// Verify order belongs to current user and is ready for pickup
$stmt = $pdo->prepare("
SELECT o.order_id, o.status, o.pickup_schedule
FROM orders o
WHERE o.order_id = ? AND o.customer_id = ? AND o.status = 'ready_for_pickup'
");
$stmt->execute([$order_id, $_SESSION['user_id']]);
$order = $stmt->fetch();
if (!$order) {
header("Location: /unimarket/customer/orders/list.php");
exit();
}
$success_message = '';
$error_message = '';
// Handle order completion
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['complete_order'])) {
try {
$pdo->beginTransaction();
// Update order status to completed
$stmt = $pdo->prepare("UPDATE orders SET status = 'completed' WHERE order_id = ?");
$stmt->execute([$order_id]);
$pdo->commit();
$success_message = 'Order has been marked as completed. Thank you for your purchase!';
// Redirect to order view page after a short delay
header("Refresh: 2; URL=/unimarket/customer/orders/view.php?order_id=" . $order_id);
} catch (Exception $e) {
$pdo->rollBack();
$error_message = 'An error occurred while completing the order. Please try again.';
}
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Complete Order - UniMarket</title>
<link rel="stylesheet" href="/unimarket/assets/css/style.css">
<link rel="stylesheet" href="/unimarket/assets/css/responsive.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
<style>
.complete-order-container {
margin-top: 30px;
}
.order-card {
background-color: white;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
padding: 20px;
margin-bottom: 30px;
}
.order-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
}
.pickup-info {
margin: 20px 0;
padding: 15px;
background-color: #f8f9fa;
border-radius: 4px;
}
.pickup-info h3 {
margin-bottom: 10px;
color: var(--primary-color);
}
.pickup-info p {
margin: 5px 0;
}
.complete-form {
margin-top: 20px;
}
.btn-complete {
background-color: var(--primary-color);
color: white;
border: none;
padding: 12px 20px;
border-radius: 4px;
cursor: pointer;
font-size: 1rem;
transition: background-color 0.3s;
}
.btn-complete:hover {
background-color: var(--primary-dark);
}
</style>
</head>
<body>
<?php include '../../includes/header.php'; ?>
<div class="container complete-order-container">
<a href="/unimarket/customer/orders/view.php?order_id=<?php echo $order_id; ?>" class="back-link">
<i class="fas fa-arrow-left"></i> Back to Order Details
</a>
<h1>Complete Order</h1>
<?php if ($success_message): ?>
<div class="alert alert-success"><?php echo $success_message; ?></div>
<?php endif; ?>
<?php if ($error_message): ?>
<div class="alert alert-error"><?php echo $error_message; ?></div>
<?php endif; ?>
<div class="order-card">
<div class="order-header">
<h2>Order #<?php echo $order['order_id']; ?></h2>
<span class="order-status status-ready">Ready for Pickup</span>
</div>
<div class="pickup-info">
<h3><i class="fas fa-info-circle"></i> Pickup Information</h3>
<p><strong>Scheduled Pickup:</strong> <?php echo date('F j, Y g:i A', strtotime($order['pickup_schedule'])); ?></p>
<p><strong>Payment Method:</strong> Pay on Pickup</p>
<p><strong>Instructions:</strong> Please bring your university ID for verification when picking up your order.</p>
</div>
<form method="POST" class="complete-form">
<p>Click the button below to mark this order as completed after you have picked up your items and made the payment.</p>
<button type="submit" name="complete_order" class="btn-complete">
<i class="fas fa-check-circle"></i> Mark Order as Completed
</button>
</form>
</div>
</div>
<?php include '../../includes/footer.php'; ?>
</body>
</html>
+281
View File
@@ -0,0 +1,281 @@
<?php
require_once '../../includes/auth_functions.php';
redirectIfNotLoggedIn();
redirectIfNotCustomer();
require_once '../../config/database.php';
// Pagination variables
$current_page = isset($_GET['page']) ? (int)$_GET['page'] : 1;
$per_page = 10;
$offset = ($current_page - 1) * $per_page;
// Add cache control headers
header("Cache-Control: no-store, no-cache, must-revalidate, max-age=0");
header("Cache-Control: post-check=0, pre-check=0", false);
header("Pragma: no-cache");
// Get total orders count
$count_stmt = $pdo->prepare("SELECT COUNT(*) FROM orders WHERE customer_id = ? AND status != 'deleted'");
$count_stmt->execute([$_SESSION['user_id']]);
$total_orders = $count_stmt->fetchColumn();
// Get orders with pagination
$stmt = $pdo->prepare("
SELECT o.order_id, o.order_date, o.total_amount, o.status, o.pickup_schedule
FROM orders o
WHERE o.customer_id = ? AND o.status != 'deleted'
ORDER BY o.order_date DESC
LIMIT ? OFFSET ?
");
$stmt->bindValue(1, $_SESSION['user_id'], PDO::PARAM_INT);
$stmt->bindValue(2, $per_page, PDO::PARAM_INT);
$stmt->bindValue(3, $offset, PDO::PARAM_INT);
$stmt->execute();
$orders = $stmt->fetchAll();
// Debug information
error_log("User ID: " . $_SESSION['user_id']);
error_log("Total Orders: " . $total_orders);
foreach ($orders as $order) {
error_log("Order ID: " . $order['order_id'] . ", Status: " . $order['status']);
}
// Calculate total pages
$total_pages = ceil($total_orders / $per_page);
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>My Orders - UniMarket</title>
<link rel="stylesheet" href="/unimarket/assets/css/style.css">
<link rel="stylesheet" href="/unimarket/assets/css/responsive.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
<style>
.back-link {
display: inline-flex;
align-items: center;
gap: 8px;
padding: 8px 16px;
background-color: #f8f9fa;
border: 1px solid #e9ecef;
border-radius: 6px;
color: #2e7d32;
text-decoration: none;
font-weight: 500;
margin-bottom: 20px;
transition: all 0.2s ease;
}
.back-link:hover {
background-color: #e9ecef;
color: #1b5e20;
text-decoration: none;
transform: translateX(-2px);
}
.back-link i {
font-size: 0.9em;
}
.orders-container {
margin-top: 30px;
}
.orders-table {
width: 100%;
border-collapse: collapse;
margin-top: 20px;
}
.orders-table th, .orders-table td {
padding: 12px 15px;
text-align: left;
border-bottom: 1px solid #eee;
}
.orders-table th {
background-color: #f5f5f5;
font-weight: 600;
}
.order-status {
display: inline-block;
padding: 5px 10px;
border-radius: 4px;
font-size: 0.9rem;
font-weight: 500;
}
.status-pending {
background-color: #fff3cd;
color: #856404;
}
.status-processing {
background-color: #cce5ff;
color: #004085;
}
.status-ready {
background-color: #d4edda;
color: #155724;
}
.status-completed {
background-color: #e2e3e5;
color: #383d41;
}
.status-cancelled {
background-color: #f8d7da;
color: #721c24;
}
.pagination {
display: flex;
justify-content: center;
margin-top: 30px;
gap: 5px;
}
.pagination a, .pagination span {
display: inline-block;
padding: 8px 15px;
border: 1px solid #ddd;
border-radius: 4px;
text-decoration: none;
color: var(--text-color);
}
.pagination a:hover {
background-color: var(--primary-color);
color: white;
border-color: var(--primary-color);
}
.pagination .current {
background-color: var(--primary-color);
color: white;
border-color: var(--primary-color);
}
.empty-orders {
text-align: center;
padding: 50px;
background-color: #f9f9f9;
border-radius: 8px;
}
@media (max-width: 768px) {
.orders-table {
display: block;
overflow-x: auto;
}
}
</style>
</head>
<body>
<?php include '../../includes/header.php'; ?>
<div class="container orders-container">
<a href="/unimarket/customer/profile.php" class="back-link">
<i class="fas fa-arrow-left"></i> Back to Profile
</a>
<h1>My Orders</h1>
<?php if (empty($orders)): ?>
<div class="empty-orders card">
<i class="fas fa-box-open" style="font-size: 3em; color: #ccc; margin-bottom: 20px;"></i>
<h3>You haven't placed any orders yet</h3>
<p>Start shopping to see your orders here</p>
<a href="/unimarket/customer/products/browse.php" class="btn">Browse Products</a>
</div>
<?php else: ?>
<table class="orders-table">
<thead>
<tr>
<th>Order #</th>
<th>Date</th>
<th>Total</th>
<th>Status</th>
<th>Pickup Schedule</th>
</tr>
</thead>
<tbody>
<?php foreach ($orders as $order): ?>
<tr>
<td>
<a href="/unimarket/customer/orders/view.php?order_id=<?php echo $order['order_id']; ?>">
#<?php echo $order['order_id']; ?>
</a>
</td>
<td><?php echo date('M j, Y', strtotime($order['order_date'])); ?></td>
<td>$<?php echo number_format($order['total_amount'], 2); ?></td>
<td>
<?php
$status_class = '';
switch ($order['status']) {
case 'pending':
$status_class = 'status-pending';
break;
case 'processing':
$status_class = 'status-processing';
break;
case 'ready_for_pickup':
$status_class = 'status-ready';
break;
case 'completed':
$status_class = 'status-completed';
break;
case 'cancelled':
$status_class = 'status-cancelled';
break;
}
?>
<span class="order-status <?php echo $status_class; ?>">
<?php echo ucfirst(str_replace('_', ' ', $order['status'])); ?>
</span>
</td>
<td>
<?php if ($order['pickup_schedule']): ?>
<?php echo date('M j, Y g:i A', strtotime($order['pickup_schedule'])); ?>
<?php else: ?>
Not scheduled
<?php endif; ?>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<?php if ($total_pages > 1): ?>
<div class="pagination">
<?php if ($current_page > 1): ?>
<a href="?page=1">First</a>
<a href="?page=<?php echo $current_page - 1; ?>">Prev</a>
<?php endif; ?>
<?php for ($i = max(1, $current_page - 2); $i <= min($current_page + 2, $total_pages); $i++): ?>
<?php if ($i == $current_page): ?>
<span class="current"><?php echo $i; ?></span>
<?php else: ?>
<a href="?page=<?php echo $i; ?>"><?php echo $i; ?></a>
<?php endif; ?>
<?php endfor; ?>
<?php if ($current_page < $total_pages): ?>
<a href="?page=<?php echo $current_page + 1; ?>">Next</a>
<a href="?page=<?php echo $total_pages; ?>">Last</a>
<?php endif; ?>
</div>
<?php endif; ?>
<?php endif; ?>
</div>
<?php include '../../includes/footer.php'; ?>
</body>
</html>
+533
View File
@@ -0,0 +1,533 @@
<?php
require_once '../../includes/auth_functions.php';
redirectIfNotLoggedIn();
redirectIfNotCustomer();
require_once '../../config/database.php';
// Get order ID from URL
if (!isset($_GET['order_id'])) {
header("Location: /unimarket/customer/orders/list.php");
exit();
}
$order_id = (int)$_GET['order_id'];
// Get order details first
$stmt = $pdo->prepare("
SELECT o.*, u.username, u.first_name, u.last_name, u.email
FROM orders o
JOIN users u ON o.customer_id = u.user_id
WHERE o.order_id = ?
");
$stmt->execute([$order_id]);
$order = $stmt->fetch();
// Verify order belongs to current user
if (!$order || $order['customer_id'] != $_SESSION['user_id']) {
header("Location: /unimarket/customer/orders/list.php");
exit();
}
// Handle pickup confirmation and cancellation
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
if (isset($_POST['confirm_pickup'])) {
try {
$stmt = $pdo->prepare("UPDATE orders SET status = 'completed' WHERE order_id = ? AND customer_id = ? AND status = 'ready'");
$stmt->execute([$order_id, $_SESSION['user_id']]);
// Refresh order data
$stmt = $pdo->prepare("
SELECT o.order_id, o.order_date, o.total_amount, o.status, o.pickup_schedule
FROM orders o
WHERE o.order_id = ?
");
$stmt->execute([$order_id]);
$order = $stmt->fetch();
$success_message = 'Order pickup confirmed! Thank you for your purchase.';
} catch (PDOException $e) {
$error_message = 'Failed to confirm pickup. Please try again.';
}
} elseif (isset($_POST['cancel_order'])) {
try {
// Only allow cancellation if order is not completed or cancelled
if ($order['status'] !== 'completed' && $order['status'] !== 'cancelled') {
$stmt = $pdo->prepare("UPDATE orders SET status = 'cancelled' WHERE order_id = ? AND customer_id = ?");
$stmt->execute([$order_id, $_SESSION['user_id']]);
// Refresh order data
$stmt = $pdo->prepare("
SELECT o.order_id, o.order_date, o.total_amount, o.status, o.pickup_schedule
FROM orders o
WHERE o.order_id = ?
");
$stmt->execute([$order_id]);
$order = $stmt->fetch();
$success_message = 'Order has been cancelled successfully.';
} else {
$error_message = 'This order cannot be cancelled.';
}
} catch (PDOException $e) {
$error_message = 'Failed to cancel order. Please try again.';
}
} elseif (isset($_POST['delete_order'])) {
try {
// Start transaction
$pdo->beginTransaction();
// Update order status to deleted
$stmt = $pdo->prepare("UPDATE orders SET status = 'deleted' WHERE order_id = ? AND customer_id = ?");
$stmt->execute([$order_id, $_SESSION['user_id']]);
// Log the deletion
error_log("Deleting order ID: " . $order_id . " for user ID: " . $_SESSION['user_id']);
// Commit transaction
$pdo->commit();
// Redirect to orders list
header("Location: /unimarket/customer/orders/list.php");
exit();
} catch (PDOException $e) {
// Rollback transaction on error
$pdo->rollBack();
error_log("Error deleting order: " . $e->getMessage());
$error_message = 'Failed to delete order. Please try again.';
}
}
}
// Get order items with seller information
$stmt = $pdo->prepare("
SELECT oi.*, p.name, p.image_path, p.price,
u.username as seller_username, u.first_name as seller_first_name, u.last_name as seller_last_name
FROM order_items oi
JOIN products p ON oi.product_id = p.product_id
JOIN users u ON p.owner_id = u.user_id
WHERE oi.order_id = ?
");
$stmt->execute([$order_id]);
$order_items = $stmt->fetchAll();
// Calculate total items
$total_items = array_sum(array_column($order_items, 'quantity'));
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Order Details - UniMarket</title>
<link rel="stylesheet" href="/unimarket/assets/css/style.css">
<link rel="stylesheet" href="/unimarket/assets/css/responsive.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
<style>
header {
position: fixed;
top: 0;
left: 0;
right: 0;
z-index: 1000;
background: rgba(46, 125, 50, 0.95);
backdrop-filter: blur(10px);
-webkit-backdrop-filter: blur(10px);
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
}
.order-details-container {
margin-top: 100px; /* Add margin to prevent content from hiding under fixed header */
}
.order-details-container {
margin-top: 30px;
}
.order-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
flex-wrap: wrap;
}
.order-info {
background-color: white;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
padding: 20px;
margin-bottom: 30px;
}
.info-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 20px;
margin-bottom: 20px;
}
.info-item {
background-color: #f8f9fa;
padding: 15px;
border-radius: 8px;
}
.info-label {
font-size: 0.9rem;
color: #666;
margin-bottom: 5px;
}
.info-value {
font-size: 1.1rem;
font-weight: 500;
}
.order-status {
display: inline-block;
padding: 8px 15px;
border-radius: 4px;
font-weight: 500;
}
.status-pending {
background-color: #fff3cd;
color: #856404;
}
.status-processing {
background-color: #cce5ff;
color: #004085;
}
.status-ready {
background-color: #d4edda;
color: #155724;
}
.status-completed {
background-color: #e2e3e5;
color: #383d41;
}
.status-cancelled {
background-color: #f8d7da;
color: #721c24;
}
.order-items {
background-color: white;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
padding: 20px;
}
.items-table {
width: 100%;
border-collapse: collapse;
margin-top: 20px;
}
.items-table th, .items-table td {
padding: 12px 15px;
text-align: left;
border-bottom: 1px solid #eee;
}
.items-table th {
background-color: #f5f5f5;
font-weight: 600;
}
.product-cell {
display: flex;
align-items: center;
gap: 15px;
}
.product-image {
width: 60px;
height: 60px;
background-color: #f8f9fa;
border-radius: 4px;
display: flex;
align-items: center;
justify-content: center;
overflow: hidden;
}
.product-image img {
max-width: 100%;
max-height: 100%;
object-fit: cover;
}
.product-info {
flex: 1;
}
.product-name {
font-weight: 500;
}
.order-total {
text-align: right;
margin-top: 20px;
font-size: 1.2rem;
font-weight: 600;
}
.back-link {
display: inline-block;
margin-bottom: 20px;
color: var(--primary-color);
text-decoration: none;
}
.back-link:hover {
text-decoration: underline;
}
.pickup-confirmation-form {
margin-top: 20px;
padding-top: 20px;
border-top: 1px solid #eee;
}
.confirm-pickup-btn {
background-color: #28a745;
color: white;
padding: 12px 24px;
font-size: 1.1rem;
display: flex;
align-items: center;
gap: 8px;
transition: background-color 0.2s;
}
.confirm-pickup-btn:hover {
background-color: #218838;
}
.confirm-pickup-btn i {
font-size: 1.2rem;
}
.order-actions {
margin-top: 20px;
display: flex;
gap: 15px;
flex-wrap: wrap;
}
.cancel-order-form {
margin-top: 20px;
}
.cancel-order-btn {
background-color: #dc3545;
color: white;
padding: 12px 24px;
font-size: 1.1rem;
display: flex;
align-items: center;
gap: 8px;
transition: background-color 0.2s;
}
.cancel-order-btn:hover {
background-color: #c82333;
}
.cancel-order-btn i {
font-size: 1.2rem;
}
.seller-username {
font-size: 0.9rem;
color: #666;
margin-top: 3px;
}
@media (max-width: 768px) {
.info-grid {
grid-template-columns: 1fr;
}
.items-table {
display: block;
overflow-x: auto;
}
.product-cell {
min-width: 200px;
}
}
</style>
</head>
<body>
<?php include '../../includes/header.php'; ?>
<div class="container order-details-container">
<a href="/unimarket/customer/profile.php" class="back-link">
<i class="fas fa-arrow-left"></i> Back to Profile
</a>
<div class="order-header">
<h1>Order #<?php echo $order['order_id']; ?></h1>
<?php
$status_class = '';
switch ($order['status']) {
case 'pending':
$status_class = 'status-pending';
break;
case 'processing':
$status_class = 'status-processing';
break;
case 'ready':
$status_class = 'status-ready';
break;
case 'completed':
$status_class = 'status-completed';
break;
case 'cancelled':
$status_class = 'status-cancelled';
break;
}
?>
<span class="order-status <?php echo $status_class; ?>">
<?php echo ucfirst(str_replace('_', ' ', $order['status'])); ?>
</span>
</div>
<div class="order-info card">
<h2>Order Information</h2>
<div class="info-grid">
<div class="info-item">
<div class="info-label">Order Number</div>
<div class="info-value">#<?php echo $order['order_id']; ?></div>
</div>
<div class="info-item">
<div class="info-label">Order Date</div>
<div class="info-value"><?php echo date('F j, Y g:i A', strtotime($order['order_date'])); ?></div>
</div>
<div class="info-item">
<div class="info-label">Order Status</div>
<div class="info-value">
<span class="order-status <?php echo $status_class; ?>">
<?php echo ucfirst(str_replace('_', ' ', $order['status'])); ?>
</span>
</div>
</div>
<div class="info-item">
<div class="info-label">Total Items</div>
<div class="info-value"><?php echo $total_items; ?> items</div>
</div>
<div class="info-item">
<div class="info-label">Total Amount</div>
<div class="info-value">₱<?php echo number_format($order['total_amount'], 2); ?></div>
</div>
<div class="info-item">
<div class="info-label">Payment Method</div>
<div class="info-value">Pay on Pickup</div>
</div>
<div class="info-item">
<div class="info-label">Pickup Schedule</div>
<div class="info-value">
<?php if ($order['pickup_schedule']): ?>
<?php echo date('F j, Y g:i A', strtotime($order['pickup_schedule'])); ?>
<?php else: ?>
<span style="color: #666;">Waiting for seller to schedule pickup time</span>
<?php endif; ?>
</div>
</div>
</div>
<div class="order-actions">
<?php if ($order['status'] === 'ready'): ?>
<form method="POST" class="pickup-confirmation-form" onsubmit="return confirm('Have you received your items and completed the payment?');">
<button type="submit" name="confirm_pickup" class="btn confirm-pickup-btn">
<i class="fas fa-check-circle"></i> Confirm Pickup
</button>
</form>
<?php endif; ?>
<?php if ($order['status'] !== 'completed' && $order['status'] !== 'cancelled'): ?>
<form method="POST" class="cancel-order-form" onsubmit="return confirm('Are you sure you want to cancel this order? This action cannot be undone.');">
<button type="submit" name="cancel_order" class="btn cancel-order-btn">
<i class="fas fa-times-circle"></i> Cancel Order
</button>
</form>
<?php endif; ?>
</div>
</div>
<div class="order-items card">
<h2>Order Items</h2>
<table class="items-table">
<thead>
<tr>
<th>Product</th>
<th>Seller</th>
<th>Price</th>
<th>Quantity</th>
<th>Subtotal</th>
</tr>
</thead>
<tbody>
<?php foreach ($order_items as $item): ?>
<tr>
<td class="product-cell">
<?php if ($item['image_path']): ?>
<div class="product-image">
<img src="/unimarket/assets/images/products/<?php echo htmlspecialchars($item['image_path']); ?>"
alt="<?php echo htmlspecialchars($item['name']); ?>">
</div>
<?php else: ?>
<div class="product-image">
<i class="fas fa-box"></i>
</div>
<?php endif; ?>
<div class="product-info">
<div class="product-name"><?php echo htmlspecialchars($item['name']); ?></div>
</div>
</td>
<td>
<?php echo htmlspecialchars($item['seller_first_name'] . ' ' . $item['seller_last_name']); ?>
<div class="seller-username">@<?php echo htmlspecialchars($item['seller_username']); ?></div>
</td>
<td>₱<?php echo number_format($item['price'], 2); ?></td>
<td><?php echo $item['quantity']; ?></td>
<td>₱<?php echo number_format($item['price'] * $item['quantity'], 2); ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<div class="order-total">
<div style="margin-bottom: 10px;">Subtotal: ₱<?php echo number_format($order['total_amount'], 2); ?></div>
<div style="margin-bottom: 10px;">Shipping: Free</div>
<div style="font-size: 1.4rem;">Total: ₱<?php echo number_format($order['total_amount'], 2); ?></div>
</div>
<?php if ($order['status'] === 'completed' || $order['status'] === 'cancelled'): ?>
<form method="POST" style="margin-top: 20px; text-align: right;">
<button type="submit" name="delete_order" class="btn btn-danger" onclick="return confirm('Are you sure you want to delete this order? This action cannot be undone.')">
<i class="fas fa-trash"></i> Delete Order
</button>
</form>
<?php endif; ?>
</div>
</div>
<?php include '../../includes/footer.php'; ?>
</body>
</html>
+335
View File
@@ -0,0 +1,335 @@
<?php
require_once '../../includes/auth_functions.php';
redirectIfNotLoggedIn();
redirectIfNotCustomer();
require_once '../../config/database.php';
// Pagination variables
$current_page = isset($_GET['page']) ? (int)$_GET['page'] : 1;
$per_page = 12;
$offset = ($current_page - 1) * $per_page;
// Search and filter variables
$search_query = isset($_GET['search']) ? trim($_GET['search']) : '';
$category_filter = isset($_GET['category']) ? trim($_GET['category']) : '';
// Base query
$query = "SELECT * FROM products WHERE stock_quantity > 0";
$count_query = "SELECT COUNT(*) FROM products WHERE stock_quantity > 0";
$params = [];
// Add search condition
if (!empty($search_query)) {
$query .= " AND (name LIKE ? OR description LIKE ?)";
$count_query .= " AND (name LIKE ? OR description LIKE ?)";
$search_param = "%$search_query%";
array_push($params, $search_param, $search_param);
}
// Add category filter
if (!empty($category_filter)) {
$query .= " AND category = ?";
$count_query .= " AND category = ?";
array_push($params, $category_filter);
}
// Add sorting
$sort = isset($_GET['sort']) ? $_GET['sort'] : 'newest';
switch ($sort) {
case 'price_low':
$query .= " ORDER BY price ASC";
break;
case 'price_high':
$query .= " ORDER BY price DESC";
break;
case 'name':
$query .= " ORDER BY name ASC";
break;
default:
$query .= " ORDER BY created_at DESC";
break;
}
// Add pagination
$query .= " LIMIT ? OFFSET ?";
array_push($params, $per_page, $offset);
// Prepare and execute the query
$stmt = $pdo->prepare($query);
foreach ($params as $key => $value) {
$paramType = is_int($value) ? PDO::PARAM_INT : PDO::PARAM_STR;
$stmt->bindValue($key + 1, $value, $paramType);
}
$stmt->execute();
$products = $stmt->fetchAll();
// Get total count for pagination
$count_stmt = $pdo->prepare($count_query);
foreach (array_slice($params, 0, count($params) - 2) as $key => $value) {
$paramType = is_int($value) ? PDO::PARAM_INT : PDO::PARAM_STR;
$count_stmt->bindValue($key + 1, $value, $paramType);
}
$count_stmt->execute();
$total_products = $count_stmt->fetchColumn();
// Get all categories for filter dropdown
$categories = $pdo->query("SELECT DISTINCT category FROM products WHERE category IS NOT NULL AND category != '' ORDER BY category")->fetchAll();
// Calculate total pages
$total_pages = ceil($total_products / $per_page);
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Browse Products - UniMarket</title>
<link rel="stylesheet" href="/unimarket/assets/css/style.css">
<link rel="stylesheet" href="/unimarket/assets/css/responsive.css">
<style>
.browse-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
flex-wrap: wrap;
}
.search-filter-container {
display: flex;
gap: 15px;
margin-bottom: 15px;
flex-wrap: wrap;
}
.search-box {
flex: 1;
min-width: 250px;
position: relative;
}
.search-box input {
width: 100%;
padding: 10px 15px;
padding-right: 40px;
border: 1px solid #ddd;
border-radius: 4px;
}
.search-box button {
position: absolute;
right: 10px;
top: 50%;
transform: translateY(-50%);
background: none;
border: none;
cursor: pointer;
color: var(--primary-color);
}
.filter-dropdown {
min-width: 200px;
}
.filter-dropdown select {
width: 100%;
padding: 10px;
border: 1px solid #ddd;
border-radius: 4px;
}
.sort-dropdown {
min-width: 150px;
}
.pagination {
display: flex;
justify-content: center;
margin-top: 30px;
gap: 5px;
}
.pagination a, .pagination span {
display: inline-block;
padding: 8px 15px;
border: 1px solid #ddd;
border-radius: 4px;
text-decoration: none;
color: var(--text-color);
}
.pagination a:hover {
background-color: var(--primary-color);
color: white;
border-color: var(--primary-color);
}
.pagination .current {
background-color: var(--primary-color);
color: white;
border-color: var(--primary-color);
}
.no-products {
text-align: center;
padding: 40px;
background-color: #f9f9f9;
border-radius: 8px;
}
.product-card .stock-info {
color: #666;
font-size: 0.9rem;
margin-bottom: 10px;
}
.product-card .stock-low {
color: #e53935;
}
</style>
</head>
<body>
<?php include '../../includes/header.php'; ?>
<div class="container">
<div class="browse-header">
<h1>Browse Products</h1>
<div class="search-filter-container">
<form method="GET" class="search-box">
<input type="text" name="search" placeholder="Search products..." value="<?php echo htmlspecialchars($search_query); ?>">
<button type="submit"><i class="fas fa-search"></i></button>
</form>
<div class="filter-dropdown">
<select name="category" onchange="this.form.submit()" form="filter-form">
<option value="">All Categories</option>
<?php foreach ($categories as $category): ?>
<option value="<?php echo htmlspecialchars($category['category']); ?>" <?php echo $category_filter === $category['category'] ? 'selected' : ''; ?>>
<?php echo htmlspecialchars($category['category']); ?>
</option>
<?php endforeach; ?>
</select>
</div>
<div class="sort-dropdown">
<select name="sort" onchange="this.form.submit()" form="filter-form">
<option value="newest" <?php echo $sort === 'newest' ? 'selected' : ''; ?>>Newest First</option>
<option value="price_low" <?php echo $sort === 'price_low' ? 'selected' : ''; ?>>Price: Low to High</option>
<option value="price_high" <?php echo $sort === 'price_high' ? 'selected' : ''; ?>>Price: High to Low</option>
<option value="name" <?php echo $sort === 'name' ? 'selected' : ''; ?>>Name: A-Z</option>
</select>
</div>
<form id="filter-form" method="GET" style="display: none;">
<input type="hidden" name="search" value="<?php echo htmlspecialchars($search_query); ?>">
</form>
</div>
</div>
<?php if (empty($products)): ?>
<div class="no-products card">
<h3>No products found</h3>
<p>Try adjusting your search or filter criteria</p>
<a href="/unimarket/customer/products/browse.php" class="btn">Reset Filters</a>
</div>
<?php else: ?>
<div class="product-grid">
<?php foreach ($products as $product): ?>
<div class="product-card card">
<a style="color: black; text-decoration: none;" href="/unimarket/customer/products/view.php?id=<?php echo $product['product_id']; ?>">
<div class="product-image">
<?php if ($product['image_path']): ?>
<img src="/unimarket/uploads/products/<?php echo $product['image_path']; ?>" alt="<?php echo htmlspecialchars($product['name']); ?>">
<?php else: ?>
<div style="display: flex; align-items: center; justify-content: center; height: 100%; color: #999;">
<i class="fas fa-box-open" style="font-size: 3rem;"></i>
</div>
<?php endif; ?>
</div>
<div class="product-info">
<h3 class="product-title"><?php echo htmlspecialchars($product['name']); ?></h3>
<p class="product-price">₱<?php echo number_format($product['price'], 2); ?></p>
<p class="stock-info <?php echo $product['stock_quantity'] < 5 ? 'stock-low' : ''; ?>">
<?php echo $product['stock_quantity'] < 5 ? 'Only ' . $product['stock_quantity'] . ' left!' : 'In stock'; ?>
</p>
<a href="/unimarket/customer/products/view.php?id=<?php echo $product['product_id']; ?>" class="btn">
<i class="fas fa-eye"></i> View Details
</a>
</div>
</a>
</div>
<?php endforeach; ?>
</div>
<?php if ($total_pages > 1): ?>
<div class="pagination">
<?php if ($current_page > 1): ?>
<a href="?<?php echo buildQueryString(['page' => 1]); ?>">First</a>
<a href="?<?php echo buildQueryString(['page' => $current_page - 1]); ?>">Prev</a>
<?php endif; ?>
<?php for ($i = max(1, $current_page - 2); $i <= min($current_page + 2, $total_pages); $i++): ?>
<?php if ($i == $current_page): ?>
<span class="current"><?php echo $i; ?></span>
<?php else: ?>
<a href="?<?php echo buildQueryString(['page' => $i]); ?>"><?php echo $i; ?></a>
<?php endif; ?>
<?php endfor; ?>
<?php if ($current_page < $total_pages): ?>
<a href="?<?php echo buildQueryString(['page' => $current_page + 1]); ?>">Next</a>
<a href="?<?php echo buildQueryString(['page' => $total_pages]); ?>">Last</a>
<?php endif; ?>
</div>
<?php endif; ?>
<?php endif; ?>
</div>
<?php include '../../includes/footer.php'; ?>
<script>
// Add to cart with quantity validation
document.querySelectorAll('form[action="/unimarket/customer/cart.php"]').forEach(form => {
form.addEventListener('submit', function(e) {
const quantityInput = this.querySelector('input[name="quantity"]');
const max = parseInt(quantityInput.getAttribute('max'));
const value = parseInt(quantityInput.value);
if (value < 1) {
e.preventDefault();
alert('Quantity must be at least 1');
quantityInput.focus();
} else if (value > max) {
e.preventDefault();
alert(`Only ${max} available in stock`);
quantityInput.focus();
}
});
});
// Helper function to build query string from current URL parameters
function buildQueryString(params) {
const searchParams = new URLSearchParams(window.location.search);
for (const key in params) {
if (params[key] !== undefined) {
searchParams.set(key, params[key]);
}
}
return searchParams.toString();
}
</script>
</body>
</html>
<?php
/**
* Helper function to build query string while preserving existing parameters
*/
function buildQueryString($new_params) {
$params = array_merge($_GET, $new_params);
return http_build_query($params);
}
?>
+546
View File
@@ -0,0 +1,546 @@
<?php
require_once '../../includes/auth_functions.php';
redirectIfNotLoggedIn();
redirectIfNotCustomer();
require_once '../../config/database.php';
// Check if product ID is provided
if (!isset($_GET['id'])) {
header("Location: /unimarket/customer/products/browse.php");
exit();
}
$product_id = (int)$_GET['id'];
// Get product details
$stmt = $pdo->prepare("SELECT p.*, u.username as seller_name
FROM products p
JOIN users u ON p.owner_id = u.user_id
WHERE p.product_id = ? AND p.stock_quantity > 0");
$stmt->execute([$product_id]);
$product = $stmt->fetch();
if (!$product) {
header("Location: /unimarket/customer/products/browse.php");
exit();
}
// Handle add to cart
$error_message = '';
$success_message = '';
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['add_to_cart'])) {
$quantity = (int)$_POST['quantity'];
if ($quantity < 1) {
$error_message = 'Quantity must be at least 1.';
} elseif ($quantity > $product['stock_quantity']) {
$error_message = "Only {$product['stock_quantity']} available in stock.";
} else {
try {
// Get or create cart for user
$stmt = $pdo->prepare("SELECT cart_id FROM cart WHERE user_id = ?");
$stmt->execute([$_SESSION['user_id']]);
$cart = $stmt->fetch();
if (!$cart) {
$stmt = $pdo->prepare("INSERT INTO cart (user_id) VALUES (?)");
$stmt->execute([$_SESSION['user_id']]);
$cart_id = $pdo->lastInsertId();
} else {
$cart_id = $cart['cart_id'];
}
// Check if product already in cart
$stmt = $pdo->prepare("SELECT * FROM cart_items WHERE cart_id = ? AND product_id = ?");
$stmt->execute([$cart_id, $product_id]);
$existing_item = $stmt->fetch();
if ($existing_item) {
// Update quantity if already in cart
$new_quantity = $existing_item['quantity'] + $quantity;
if ($new_quantity > $product['stock_quantity']) {
$error_message = "Cannot add more than available stock.";
} else {
$stmt = $pdo->prepare("UPDATE cart_items SET quantity = ? WHERE cart_item_id = ?");
$stmt->execute([$new_quantity, $existing_item['cart_item_id']]);
$success_message = 'Product quantity updated in cart!';
}
} else {
// Add new item to cart
$stmt = $pdo->prepare("INSERT INTO cart_items (cart_id, product_id, quantity) VALUES (?, ?, ?)");
$stmt->execute([$cart_id, $product_id, $quantity]);
$success_message = 'Product added to cart!';
}
} catch (PDOException $e) {
$error_message = 'Failed to add product to cart. Please try again.';
}
}
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title><?php echo htmlspecialchars($product['name']); ?> - UniMarket</title>
<link rel="stylesheet" href="/unimarket/assets/css/style.css">
<link rel="stylesheet" href="/unimarket/assets/css/responsive.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
<script src="/unimarket/assets/js/notifications.js"></script>
<style>
.product-detail-container {
margin-top: 30px;
}
.product-main {
display: grid;
grid-template-columns: 1fr;
gap: 30px;
margin-bottom: 40px;
}
@media (min-width: 768px) {
.product-main {
grid-template-columns: 1fr 1fr;
}
}
.product-gallery {
background-color: #f9f9f9;
border-radius: 8px;
padding: 20px;
text-align: center;
}
.product-image {
max-width: 100%;
max-height: 400px;
margin-bottom: 20px;
}
.product-info {
padding: 20px;
}
.product-title {
font-size: 2rem;
margin-bottom: 10px;
color: var(--primary-dark);
}
.product-seller {
color: #666;
margin-bottom: 20px;
font-size: 1.1rem;
}
.product-price {
font-size: 1.8rem;
font-weight: bold;
color: var(--primary-color);
margin-bottom: 20px;
}
.product-stock {
margin-bottom: 20px;
font-size: 1.1rem;
}
.stock-high {
color: #2e7d32;
}
.stock-low {
color: #e53935;
}
.product-description {
margin-bottom: 30px;
line-height: 1.6;
}
.add-to-cart-form {
display: flex;
gap: 10px;
flex-wrap: wrap;
align-items: center;
}
.quantity-input {
width: 80px;
padding: 10px;
border: 1px solid #ddd;
border-radius: 4px;
text-align: center;
}
.back-link {
display: inline-block;
margin-bottom: 20px;
color: var(--primary-color);
text-decoration: none;
}
.back-link:hover {
text-decoration: underline;
}
.product-meta {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 20px;
margin-top: 30px;
padding-top: 30px;
border-top: 1px solid #eee;
}
.meta-item {
background-color: #f5f5f5;
padding: 15px;
border-radius: 8px;
}
.meta-label {
font-weight: 600;
color: #666;
margin-bottom: 5px;
}
.product-sizes {
margin-bottom: 20px;
}
.size-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(80px, 1fr));
gap: 10px;
margin-top: 10px;
}
.size-item {
border: 1px solid #ddd;
border-radius: 4px;
padding: 8px;
text-align: center;
background-color: #f9f9f9;
cursor: pointer;
transition: all 0.3s ease;
}
.size-item.in-stock {
border-color: #2e7d32;
background-color: #e8f5e9;
}
.size-item.in-stock:hover {
background-color: #c8e6c9;
transform: translateY(-2px);
}
.size-item.selected {
background-color: #2e7d32;
color: white;
}
.size-item.out-of-stock {
border-color: #c62828;
background-color: #ffebee;
opacity: 0.7;
cursor: not-allowed;
}
.size-label {
display: block;
font-weight: bold;
font-size: 1.1em;
}
.size-quantity {
display: block;
font-size: 0.9em;
color: #666;
}
.size-selection {
margin-bottom: 15px;
}
.size-selection label {
display: block;
margin-bottom: 5px;
font-weight: 500;
}
.size-selection select {
width: 100%;
padding: 10px;
border: 1px solid #ddd;
border-radius: 4px;
font-size: 1rem;
margin-bottom: 10px;
}
/* Add to cart modal styles */
.cart-modal {
display: none;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.5);
z-index: 2000;
justify-content: center;
align-items: center;
}
.cart-modal-content {
background: white;
padding: 30px;
border-radius: 8px;
text-align: center;
max-width: 400px;
width: 90%;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
}
.cart-modal h2 {
margin-bottom: 20px;
color: var(--primary-dark);
}
.cart-modal-buttons {
display: flex;
justify-content: center;
gap: 15px;
margin-top: 25px;
}
.cart-modal-buttons button {
padding: 10px 25px;
border: none;
border-radius: 4px;
cursor: pointer;
font-weight: 500;
transition: all 0.3s ease;
}
.cart-modal-buttons .btn-cancel {
background: #e0e0e0;
color: #333;
}
.cart-modal-buttons .btn-cancel:hover {
background: #d0d0d0;
}
.cart-modal-buttons .btn-confirm {
background: var(--primary-color);
color: white;
}
.cart-modal-buttons .btn-confirm:hover {
background: var(--primary-dark);
}
</style>
</head>
<body>
<!-- Add to cart modal HTML -->
<div class="cart-modal" id="cartModal">
<div class="cart-modal-content">
<h2>Add to Cart</h2>
<p>Are you sure you want to add this item to your cart?</p>
<div class="cart-modal-buttons">
<button class="btn-cancel" onclick="closeCartModal()">Cancel</button>
<button class="btn-confirm" onclick="confirmAddToCart()">Add to Cart</button>
</div>
</div>
</div>
<?php include '../../includes/header.php'; ?>
<div class="container product-detail-container">
<a href="/unimarket/customer/products/browse.php" class="back-link">
<i class="fas fa-arrow-left"></i> Back to Products
</a>
<?php if ($success_message): ?>
<div class="alert alert-success"><?php echo $success_message; ?></div>
<?php endif; ?>
<?php if ($error_message): ?>
<div class="alert alert-error"><?php echo $error_message; ?></div>
<?php endif; ?>
<div class="product-main card">
<div class="product-gallery">
<?php if ($product['image_path']): ?>
<img src="/unimarket/uploads/products/<?php echo $product['image_path']; ?>"
alt="<?php echo htmlspecialchars($product['name']); ?>"
class="product-image">
<?php else: ?>
<div style="height: 400px; display: flex; align-items: center; justify-content: center;">
<i class="fas fa-box-open" style="font-size: 5rem; color: #ccc;"></i>
</div>
<?php endif; ?>
</div>
<div class="product-info">
<h1 class="product-title"><?php echo htmlspecialchars($product['name']); ?></h1>
<p class="product-seller">Sold by <?php echo htmlspecialchars($product['seller_name']); ?></p>
<p class="product-price">₱<?php echo number_format($product['price'], 2); ?></p>
<?php if ($product['category'] === 'clothes' && isset($product['size_data']) && $product['size_data']): ?>
<?php
$size_data = json_decode($product['size_data'], true);
if ($size_data && is_array($size_data)):
?>
<div class="product-sizes">
<h3>Available Sizes</h3>
<div class="size-grid">
<?php foreach ($size_data as $size => $quantity): ?>
<div class="size-item <?php echo $quantity > 0 ? 'in-stock' : 'out-of-stock'; ?>"
data-size="<?php echo htmlspecialchars($size); ?>"
onclick="<?php echo $quantity > 0 ? 'selectSize(this)' : ''; ?>">
<span class="size-label"><?php echo htmlspecialchars($size); ?></span>
</div>
<?php endforeach; ?>
</div>
</div>
<?php endif; ?>
<?php endif; ?>
<p class="product-stock <?php echo $product['stock_quantity'] < 5 ? 'stock-low' : 'stock-high'; ?>">
<?php if ($product['stock_quantity'] < 5): ?>
Only <?php echo $product['stock_quantity']; ?> left in stock!
<?php else: ?>
In stock (<?php echo $product['stock_quantity']; ?> available)
<?php endif; ?>
</p>
<div class="product-description">
<h3>Description</h3>
<p><?php echo nl2br(htmlspecialchars($product['description'])); ?></p>
</div>
<form method="POST" action="../../ajax/add_to_cart.php" class="add-to-cart-form" id="addToCartForm">
<input type="hidden" name="product_id" value="<?php echo $product['product_id']; ?>">
<?php if ($product['category'] === 'clothes' && isset($product['size_data']) && $product['size_data']): ?>
<input type="hidden" name="size" id="selectedSize" required>
<?php endif; ?>
<input type="number" name="quantity" value="1" min="1" max="<?php echo $product['stock_quantity']; ?>"
class="quantity-input">
<button type="button" onclick="openCartModal()" class="btn">
<i class="fas fa-shopping-cart"></i> Add to Cart
</button>
</form>
</div>
</div>
<div class="product-meta">
<div class="meta-item card">
<div class="meta-label">Category</div>
<div><?php echo $product['category'] ? htmlspecialchars($product['category']) : 'No category specified'; ?></div>
</div>
<div class="meta-item card">
<div class="meta-label">Pickup Information</div>
<div>Available for on-campus pickup</div>
</div>
<div class="meta-item card">
<div class="meta-label">Payment</div>
<div>Pay when you pickup</div>
</div>
</div>
</div>
<?php include '../../includes/footer.php'; ?>
<script>
// Add to cart modal functions
function openCartModal() {
const form = document.getElementById('addToCartForm');
const sizeInput = document.getElementById('selectedSize');
if (sizeInput && !sizeInput.value) {
showToast('Please select a size', 'error');
return;
}
document.getElementById('cartModal').style.display = 'flex';
}
function closeCartModal() {
document.getElementById('cartModal').style.display = 'none';
}
function confirmAddToCart() {
const form = document.getElementById('addToCartForm');
const formData = new FormData(form);
fetch(form.action, {
method: 'POST',
body: formData
})
.then(response => response.json())
.then(data => {
if (data.success) {
// Update cart count in header
const cartCount = document.querySelector('.cart-count');
if (cartCount) {
cartCount.textContent = data.cart_count;
}
// Show success message
showToast('Item added to cart successfully!', 'success');
closeCartModal();
} else {
showToast(data.message || 'Failed to add item to cart', 'error');
}
})
.catch(error => {
console.error('Error:', error);
showToast('An error occurred while adding to cart', 'error');
});
}
// Close modal when clicking outside
document.getElementById('cartModal').addEventListener('click', function(e) {
if (e.target === this) {
closeCartModal();
}
});
// Quantity input validation
const quantityInput = document.querySelector('.quantity-input');
quantityInput.addEventListener('change', function() {
const max = parseInt(this.getAttribute('max'));
const value = parseInt(this.value);
if (value < 1) {
this.value = 1;
} else if (value > max) {
this.value = max;
alert(`Only ${max} available in stock`);
}
});
function selectSize(element) {
// Remove selected class from all size items
document.querySelectorAll('.size-item').forEach(item => {
item.classList.remove('selected');
});
// Add selected class to clicked item
element.classList.add('selected');
// Update hidden input value
document.getElementById('selectedSize').value = element.dataset.size;
}
</script>
</body>
</html>
+848
View File
@@ -0,0 +1,848 @@
<?php
require_once '../includes/auth_functions.php';
redirectIfNotLoggedIn();
redirectIfNotCustomer();
require_once '../config/database.php';
// Initialize variables
$success_message = '';
$error_message = '';
$user = [];
$orders = [];
// Get user details
$stmt = $pdo->prepare("SELECT * FROM users WHERE user_id = ?");
$stmt->execute([$_SESSION['user_id']]);
$user = $stmt->fetch();
// Get recent orders
$stmt = $pdo->prepare("
SELECT o.order_id, o.order_date, o.total_amount, o.status, o.pickup_schedule
FROM orders o
WHERE o.customer_id = ?
ORDER BY o.order_date DESC
LIMIT 5
");
$stmt->execute([$_SESSION['user_id']]);
$orders = $stmt->fetchAll();
// Handle profile updates
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['update_profile'])) {
$first_name = trim($_POST['first_name']);
$last_name = trim($_POST['last_name']);
$email = trim($_POST['email']);
$phone = trim($_POST['phone']);
$university_id = trim($_POST['university_id']);
// Validate inputs
if (empty($first_name) || empty($last_name) || empty($email) || empty($university_id)) {
$error_message = 'Please fill in all required fields.';
} elseif (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
$error_message = 'Please enter a valid email address.';
} else {
try {
// Check if email is already taken by another user
$stmt = $pdo->prepare("SELECT user_id FROM users WHERE email = ? AND user_id != ?");
$stmt->execute([$email, $_SESSION['user_id']]);
if ($stmt->fetch()) {
$error_message = 'This email is already registered to another account.';
} else {
// Update profile
$stmt = $pdo->prepare("
UPDATE users
SET first_name = ?, last_name = ?, email = ?, phone = ?, university_id = ?
WHERE user_id = ?
");
$stmt->execute([
$first_name,
$last_name,
$email,
$phone,
$university_id,
$_SESSION['user_id']
]);
$success_message = 'Profile updated successfully!';
// Refresh user data
$stmt = $pdo->prepare("SELECT * FROM users WHERE user_id = ?");
$stmt->execute([$_SESSION['user_id']]);
$user = $stmt->fetch();
}
} catch (PDOException $e) {
$error_message = 'An error occurred while updating your profile. Please try again.';
}
}
}
// Handle password changes
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['change_password'])) {
$current_password = $_POST['current_password'];
$new_password = $_POST['new_password'];
$confirm_password = $_POST['confirm_password'];
if (empty($current_password)) {
$error_message = 'Please enter your current password.';
} elseif (empty($new_password) || empty($confirm_password)) {
$error_message = 'Please enter and confirm your new password.';
} elseif ($new_password !== $confirm_password) {
$error_message = 'New passwords do not match.';
} elseif (strlen($new_password) < 8) {
$error_message = 'Password must be at least 8 characters long.';
} else {
// Verify current password
$stmt = $pdo->prepare("SELECT password FROM users WHERE user_id = ?");
$stmt->execute([$_SESSION['user_id']]);
$db_password = $stmt->fetchColumn();
if (password_verify($current_password, $db_password)) {
// Update password
$hashed_password = password_hash($new_password, PASSWORD_DEFAULT);
$stmt = $pdo->prepare("UPDATE users SET password = ? WHERE user_id = ?");
$stmt->execute([$hashed_password, $_SESSION['user_id']]);
$success_message = 'Password changed successfully!';
} else {
$error_message = 'Current password is incorrect.';
}
}
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>My Profile - UniMarket</title>
<link rel="stylesheet" href="/unimarket/assets/css/style.css">
<link rel="stylesheet" href="/unimarket/assets/css/responsive.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
<style>
.profile-container {
display: grid;
grid-template-columns: 1fr;
gap: 30px;
margin-top: 30px;
}
@media (min-width: 992px) {
.profile-container {
grid-template-columns: 1fr 1fr;
}
}
.profile-section {
background-color: white;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
padding: 25px;
}
.section-title {
font-size: 1.5rem;
margin-bottom: 20px;
color: var(--primary-color);
border-bottom: 2px solid var(--primary-light);
padding-bottom: 10px;
}
.profile-info {
display: grid;
grid-template-columns: 100px 1fr;
gap: 15px;
align-items: center;
margin-bottom: 20px;
}
.profile-avatar {
width: 100px;
height: 100px;
border-radius: 50%;
background-color: var(--primary-light);
display: flex;
align-items: center;
justify-content: center;
color: white;
font-size: 2.5rem;
}
.profile-details h3 {
margin-bottom: 5px;
font-size: 1.3rem;
}
.profile-details p {
color: #666;
margin-bottom: 5px;
}
.form-group {
margin-bottom: 20px;
}
.form-group label {
display: block;
margin-bottom: 8px;
font-weight: 500;
}
.form-group input {
width: 100%;
padding: 10px;
border: 1px solid #ddd;
border-radius: 4px;
font-size: 1rem;
}
.btn-update {
display: inline-block;
padding: 12px 24px;
background: var(--primary-color);
color: white;
border: none;
border-radius: 8px;
cursor: pointer;
text-decoration: none;
font-size: 1rem;
font-weight: 500;
transition: all 0.3s ease;
position: relative;
overflow: hidden;
}
.btn-update::after {
content: '';
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: linear-gradient(45deg, transparent, rgba(255, 255, 255, 0.1), transparent);
transform: translateX(-100%);
transition: transform 0.6s ease;
}
.btn-update:hover {
background: var(--primary-dark);
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(46, 125, 50, 0.2);
}
.btn-update:hover::after {
transform: translateX(100%);
}
.btn-secondary {
background-color: #757575;
}
.btn-secondary:hover {
background-color: #616161;
}
.orders-table {
width: 100%;
border-collapse: collapse;
margin-top: 20px;
}
.orders-table th, .orders-table td {
padding: 12px 15px;
text-align: left;
border-bottom: 1px solid #eee;
}
.orders-table th {
background-color: #f5f5f5;
}
.order-status {
display: inline-block;
padding: 5px 10px;
border-radius: 4px;
font-size: 0.9rem;
font-weight: 500;
}
#orders-ids{
text-decoration: none;
}
#orders-ids:hover{
color: green;
text-decoration: underline;
}
.status-pending {
background-color: #fff3cd;
color: #856404;
}
.status-processing {
background-color: #cce5ff;
color: #004085;
}
.status-ready {
background-color: #d4edda;
color: #155724;
}
.status-completed {
background-color: #e2e3e5;
color: #383d41;
}
.status-cancelled {
background-color: #f8d7da;
color: #721c24;
}
.view-all {
display: block;
text-align: right;
margin-top: 15px;
color: var(--primary-color);
text-decoration: none;
}
.view-all:hover {
text-decoration: underline;
}
.password-strength {
height: 5px;
background-color: #eee;
margin-top: 5px;
border-radius: 3px;
overflow: hidden;
}
.password-strength-bar {
height: 100%;
width: 0%;
background-color: #ff0000;
transition: width 0.3s, background-color 0.3s;
}
@media (max-width: 768px) {
.profile-info {
grid-template-columns: 1fr;
text-align: center;
}
.profile-avatar {
margin: 0 auto;
}
.orders-table {
display: block;
overflow-x: auto;
}
}
/* Modal Styles */
.modal {
display: none;
position: fixed;
z-index: 1000;
left: 0;
top: 0;
width: 100%;
height: 100%;
background-color: rgba(0,0,0,0.5);
}
.modal-content {
background-color: #fefefe;
margin: 15% auto;
padding: 20px;
border: 1px solid #888;
width: 80%;
max-width: 500px;
border-radius: 8px;
position: relative;
}
.close {
color: #aaa;
float: right;
font-size: 28px;
font-weight: bold;
cursor: pointer;
}
.close:hover,
.close:focus {
color: black;
text-decoration: none;
}
.button-group {
display: flex;
gap: 10px;
margin-top: 20px;
}
.profile-field {
display: flex;
align-items: center;
gap: 10px;
margin-bottom: 15px;
padding: 10px;
border-radius: 8px;
background: rgba(255, 255, 255, 0.5);
transition: all 0.3s ease;
}
.profile-field:hover {
background: rgba(255, 255, 255, 0.8);
}
.field-label {
font-weight: 500;
min-width: 120px;
color: #666;
}
.field-value {
flex: 1;
}
.edit-btn {
background: none;
border: none;
color: var(--primary-color);
cursor: pointer;
padding: 5px;
border-radius: 4px;
transition: all 0.3s ease;
display: flex;
align-items: center;
justify-content: center;
}
.edit-btn:hover {
background: rgba(46, 125, 50, 0.1);
transform: scale(1.1);
}
.edit-btn i {
font-size: 1rem;
}
.field-input {
display: none;
width: 100%;
padding: 8px;
border: 1px solid #ddd;
border-radius: 4px;
font-size: 1rem;
}
.field-input.active {
display: block;
}
.field-value.hidden {
display: none;
}
.save-btn {
display: none;
background: var(--primary-color);
color: white;
border: none;
padding: 5px 10px;
border-radius: 4px;
cursor: pointer;
font-size: 0.9rem;
transition: all 0.3s ease;
}
.save-btn.active {
display: inline-block;
}
.save-btn:hover {
background: var(--primary-dark);
}
.view-all-orders {
display: inline-block;
margin-top: 20px;
padding: 12px 24px;
background: var(--primary-color);
color: white;
text-decoration: none;
border-radius: 8px;
cursor: pointer;
font-size: 1rem;
font-weight: 500;
transition: all 0.3s ease;
position: relative;
overflow: hidden;
}
.view-all-orders::after {
content: '';
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: linear-gradient(45deg, transparent, rgba(255, 255, 255, 0.1), transparent);
transform: translateX(-100%);
transition: transform 0.6s ease;
}
.view-all-orders:hover {
background: var(--primary-dark);
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(46, 125, 50, 0.2);
}
.view-all-orders:hover::after {
transform: translateX(100%);
}
/* Add update profile modal styles */
.update-modal {
display: none;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.5);
z-index: 2000;
justify-content: center;
align-items: center;
}
.update-modal-content {
background: white;
padding: 30px;
border-radius: 8px;
text-align: center;
max-width: 400px;
width: 90%;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
}
.update-modal h2 {
margin-bottom: 20px;
color: var(--primary-dark);
}
.update-modal-buttons {
display: flex;
justify-content: center;
gap: 15px;
margin-top: 25px;
}
.update-modal-buttons button {
padding: 10px 25px;
border: none;
border-radius: 4px;
cursor: pointer;
font-weight: 500;
transition: all 0.3s ease;
}
.update-modal-buttons .btn-cancel {
background: #e0e0e0;
color: #333;
}
.update-modal-buttons .btn-cancel:hover {
background: #d0d0d0;
}
.update-modal-buttons .btn-confirm {
background: var(--primary-color);
color: white;
}
.update-modal-buttons .btn-confirm:hover {
background: var(--primary-dark);
}
</style>
</head>
<body>
<!-- Add update profile modal HTML -->
<div class="update-modal" id="updateModal">
<div class="update-modal-content">
<h2>Confirm Profile Update</h2>
<p>Are you sure you want to update your profile information?</p>
<div class="update-modal-buttons">
<button class="btn-cancel" onclick="closeUpdateModal()">Cancel</button>
<button class="btn-confirm" onclick="confirmUpdate()">Update Profile</button>
</div>
</div>
</div>
<?php include '../includes/header.php'; ?>
<div class="container">
<h1>My Profile</h1>
<?php if ($success_message): ?>
<div class="alert alert-success"><?php echo $success_message; ?></div>
<?php endif; ?>
<?php if ($error_message): ?>
<div class="alert alert-error"><?php echo $error_message; ?></div>
<?php endif; ?>
<div class="profile-container">
<div class="profile-section">
<h2 class="section-title">Profile Information</h2>
<div class="profile-info">
<div class="profile-avatar">
<i class="fas fa-user"></i>
</div>
<div class="profile-details">
<h3><?php echo htmlspecialchars($user['first_name'] . ' ' . htmlspecialchars($user['last_name'])); ?></h3>
<p><?php echo htmlspecialchars($user['email']); ?></p>
<p>Member since <?php echo date('F Y', strtotime($user['created_at'])); ?></p>
</div>
</div>
<form method="POST" id="profileForm">
<div class="profile-field">
<span class="field-label">First Name</span>
<span class="field-value"><?php echo htmlspecialchars($user['first_name']); ?></span>
<input type="text" name="first_name" class="field-input" value="<?php echo htmlspecialchars($user['first_name']); ?>" required>
<button type="button" class="edit-btn" onclick="toggleEdit(this)">
<i class="fas fa-edit"></i>
</button>
<button type="button" class="save-btn" onclick="saveField(this)">Save</button>
</div>
<div class="profile-field">
<span class="field-label">Last Name</span>
<span class="field-value"><?php echo htmlspecialchars($user['last_name']); ?></span>
<input type="text" name="last_name" class="field-input" value="<?php echo htmlspecialchars($user['last_name']); ?>" required>
<button type="button" class="edit-btn" onclick="toggleEdit(this)">
<i class="fas fa-edit"></i>
</button>
<button type="button" class="save-btn" onclick="saveField(this)">Save</button>
</div>
<div class="profile-field">
<span class="field-label">Email</span>
<span class="field-value"><?php echo htmlspecialchars($user['email']); ?></span>
<input type="email" name="email" class="field-input" value="<?php echo htmlspecialchars($user['email']); ?>" required>
<button type="button" class="edit-btn" onclick="toggleEdit(this)">
<i class="fas fa-edit"></i>
</button>
<button type="button" class="save-btn" onclick="saveField(this)">Save</button>
</div>
<div class="profile-field">
<span class="field-label">Phone Number</span>
<span class="field-value"><?php echo htmlspecialchars($user['phone']); ?></span>
<input type="tel" name="phone" class="field-input" value="<?php echo htmlspecialchars($user['phone']); ?>">
<button type="button" class="edit-btn" onclick="toggleEdit(this)">
<i class="fas fa-edit"></i>
</button>
<button type="button" class="save-btn" onclick="saveField(this)">Save</button>
</div>
<div class="profile-field">
<span class="field-label">University ID</span>
<span class="field-value"><?php echo htmlspecialchars($user['university_id']); ?></span>
<input type="text" name="university_id" class="field-input" value="<?php echo htmlspecialchars($user['university_id']); ?>" required>
<button type="button" class="edit-btn" onclick="toggleEdit(this)">
<i class="fas fa-edit"></i>
</button>
<button type="button" class="save-btn" onclick="saveField(this)">Save</button>
</div>
<div class="button-group">
<button type="button" class="btn-update" onclick="openUpdateModal()">Update Profile</button>
<button type="button" class="btn-update btn-secondary" onclick="openPasswordModal()">Change Password</button>
</div>
</form>
</div>
<div class="profile-section">
<h2 class="section-title">Recent Orders</h2>
<?php if (empty($orders)): ?>
<p>You haven't placed any orders yet.</p>
<?php else: ?>
<table class="orders-table">
<thead>
<tr>
<th>Order ID</th>
<th>Date</th>
<th>Amount</th>
<th>Status</th>
<th>Pickup Schedule</th>
</tr>
</thead>
<tbody>
<?php foreach ($orders as $order): ?>
<tr>
<td><a href="order_details.php?id=<?php echo $order['order_id']; ?>" id="orders-ids">#<?php echo $order['order_id']; ?></a></td>
<td><?php echo date('M d, Y', strtotime($order['order_date'])); ?></td>
<td>₱<?php echo number_format($order['total_amount'], 2); ?></td>
<td>
<span class="order-status status-<?php echo strtolower($order['status']); ?>">
<?php echo ucfirst($order['status']); ?>
</span>
</td>
<td><?php echo date('M d, Y h:i A', strtotime($order['pickup_schedule'])); ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<a href="/unimarket/customer/orders/list.php" class="view-all-orders">
<i class="fas fa-list"></i> View All Orders
</a>
<?php endif; ?>
</div>
</div>
</div>
<!-- Password Change Modal -->
<div id="passwordModal" class="modal">
<div class="modal-content">
<span class="close">&times;</span>
<h2>Change Password</h2>
<form method="POST" id="passwordForm">
<div class="form-group">
<label for="current_password">Current Password</label>
<input type="password" id="current_password" name="current_password" required>
</div>
<div class="form-group">
<label for="new_password">New Password</label>
<input type="password" id="new_password" name="new_password" required>
<div class="password-strength">
<div class="password-strength-bar" id="passwordStrengthBar"></div>
</div>
</div>
<div class="form-group">
<label for="confirm_password">Confirm New Password</label>
<input type="password" id="confirm_password" name="confirm_password" required>
</div>
<button type="submit" name="change_password" class="btn-update">Change Password</button>
</form>
</div>
</div>
<script>
// Modal functionality
const modal = document.getElementById('passwordModal');
const closeBtn = document.getElementsByClassName('close')[0];
function openPasswordModal() {
modal.style.display = "block";
}
closeBtn.onclick = function() {
modal.style.display = "none";
}
window.onclick = function(event) {
if (event.target == modal) {
modal.style.display = "none";
}
}
// Profile field editing functionality
function toggleEdit(button) {
const field = button.closest('.profile-field');
const value = field.querySelector('.field-value');
const input = field.querySelector('.field-input');
const saveBtn = field.querySelector('.save-btn');
value.classList.add('hidden');
input.classList.add('active');
saveBtn.classList.add('active');
button.style.display = 'none';
}
function saveField(button) {
const field = button.closest('.profile-field');
const value = field.querySelector('.field-value');
const input = field.querySelector('.field-input');
const editBtn = field.querySelector('.edit-btn');
// Update the displayed value
value.textContent = input.value;
// Hide input and save button, show value and edit button
value.classList.remove('hidden');
input.classList.remove('active');
button.classList.remove('active');
editBtn.style.display = 'flex';
}
// Password strength indicator
const passwordInput = document.getElementById('new_password');
const strengthBar = document.getElementById('passwordStrengthBar');
passwordInput.addEventListener('input', function() {
const password = this.value;
let strength = 0;
if (password.length >= 8) strength += 1;
if (password.match(/[a-z]/)) strength += 1;
if (password.match(/[A-Z]/)) strength += 1;
if (password.match(/[0-9]/)) strength += 1;
if (password.match(/[^a-zA-Z0-9]/)) strength += 1;
const width = strength * 20;
let color;
if (strength <= 1) color = '#ff0000';
else if (strength <= 3) color = '#ff9900';
else color = '#00aa00';
strengthBar.style.width = width + '%';
strengthBar.style.backgroundColor = color;
});
// Form validation
document.getElementById('passwordForm').addEventListener('submit', function(e) {
const newPassword = document.getElementById('new_password').value;
const confirmPassword = document.getElementById('confirm_password').value;
if (newPassword !== confirmPassword) {
e.preventDefault();
alert('Passwords do not match!');
}
});
// Add update profile modal functions
function openUpdateModal() {
document.getElementById('updateModal').style.display = 'flex';
}
function closeUpdateModal() {
document.getElementById('updateModal').style.display = 'none';
}
function confirmUpdate() {
document.getElementById('profileForm').submit();
}
// Close modal when clicking outside
document.getElementById('updateModal').addEventListener('click', function(e) {
if (e.target === this) {
closeUpdateModal();
}
});
</script>
</body>
</html>
+7
View File
@@ -0,0 +1,7 @@
-- Truncate orders table and reset auto-increment
TRUNCATE TABLE orders;
TRUNCATE TABLE order_items;
ALTER TABLE orders AUTO_INCREMENT = 1;
ALTER TABLE order_items AUTO_INCREMENT = 1;
ALTER TABLE cart_items ADD COLUMN size VARCHAR(10) DEFAULT NULL;
+44
View File
@@ -0,0 +1,44 @@
/unimarket
│
├── assets/
│ ├── css/
│ │ ├── style.css okay na
│ │ └── responsive.css okay na
│ ├── js/
│ │ └── script.js okay na
│ └── images/
│ └── logo.png
│
├── config/
│ └── database.php okay na
│
├── includes/
│ ├── header.php okay na
│ ├── footer.php okay na
│ └── auth_functions.php okay na
│
├── owner/
│ ├── dashboard.php okay na
│ ├── products/
│ │ ├── add.php
│ │ ├── edit.php
│ │ └── list.php
│ └── orders/
│ ├── list.php
│ └── schedule_pickup.php
│
├── customer/
│ ├── profile.php okay na
│ ├── cart.php okay na
│ ├── orders/
│ │ ├── list.php
│ │ └── view.php
│ └── products/
│ └── browse.php okay na
│
├── auth/
│ ├── login.php okay na
│ ├── register.php okay na
│ └── logout.php okay na
│
└── index.php okay na
+260
View File
@@ -0,0 +1,260 @@
<?php
if (session_status() === PHP_SESSION_NONE) {
session_start();
}
require_once __DIR__ . '/../config/database.php';
/**
* Check if a user is logged in
* @return bool True if user is logged in, false otherwise
*/
function isLoggedIn() {
return isset($_SESSION['user_id']);
}
/**
* Check if the logged in user is an owner
* @return bool True if user is an owner, false otherwise
*/
function isOwner() {
return isLoggedIn() && $_SESSION['role'] === 'owner';
}
/**
* Check if the logged in user is a customer
* @return bool True if user is a customer, false otherwise
*/
function isCustomer() {
return isLoggedIn() && $_SESSION['role'] === 'customer';
}
/**
* Check if the logged in user is an admin
* @return bool True if user is an admin, false otherwise
*/
function isAdmin() {
return isLoggedIn() && $_SESSION['role'] === 'admin';
}
/**
* Redirect to login page if not logged in
*/
function redirectIfNotLoggedIn() {
if (!isLoggedIn()) {
$_SESSION['redirect_url'] = $_SERVER['REQUEST_URI'];
header("Location: /unimarket/auth/login.php");
exit();
}
}
/**
* Redirect to homepage if not an owner
*/
function redirectIfNotOwner() {
if (!isOwner()) {
header("Location: /unimarket/index.php");
exit();
}
}
/**
* Redirect to homepage if not a customer
*/
function redirectIfNotCustomer() {
if (!isCustomer()) {
header("Location: /unimarket/index.php");
exit();
}
}
/**
* Redirect to homepage if not an admin
*/
function redirectIfNotAdmin() {
if (!isAdmin()) {
header("Location: /unimarket/index.php");
exit();
}
}
/**
* Get the current user's ID
* @return int|null User ID if logged in, null otherwise
*/
function getUserId() {
return $_SESSION['user_id'] ?? null;
}
/**
* Get the current user's role
* @return string|null User role if logged in, null otherwise
*/
function getUserRole() {
return $_SESSION['role'] ?? null;
}
/**
* Get the current user's username
* @return string|null Username if logged in, null otherwise
*/
function getUsername() {
return $_SESSION['username'] ?? null;
}
/**
* Verify if the provided password matches the user's password
* @param int $user_id User ID to verify
* @param string $password Password to verify
* @return bool True if password matches, false otherwise
*/
function verifyPassword($user_id, $password) {
global $pdo;
try {
$stmt = $pdo->prepare("SELECT password FROM users WHERE user_id = ?");
$stmt->execute([$user_id]);
$user = $stmt->fetch();
return $user && password_verify($password, $user['password']);
} catch (PDOException $e) {
return false;
}
}
/**
* Update user's password
* @param int $user_id User ID
* @param string $new_password New password
* @return bool True if update was successful, false otherwise
*/
function updatePassword($user_id, $new_password) {
global $pdo;
try {
$hashed_password = password_hash($new_password, PASSWORD_DEFAULT);
$stmt = $pdo->prepare("UPDATE users SET password = ? WHERE user_id = ?");
return $stmt->execute([$hashed_password, $user_id]);
} catch (PDOException $e) {
return false;
}
}
/**
* Generate a CSRF token and store it in session
* @return string Generated CSRF token
*/
function generateCsrfToken() {
if (empty($_SESSION['csrf_token'])) {
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
}
return $_SESSION['csrf_token'];
}
/**
* Verify a CSRF token
* @param string $token Token to verify
* @return bool True if token is valid, false otherwise
*/
function verifyCsrfToken($token) {
return isset($_SESSION['csrf_token']) && hash_equals($_SESSION['csrf_token'], $token);
}
/**
* Check if the current user owns a specific product
* @param int $product_id Product ID to check
* @return bool True if user owns the product, false otherwise
*/
function ownsProduct($product_id) {
if (!isOwner()) return false;
global $pdo;
try {
$stmt = $pdo->prepare("SELECT product_id FROM products WHERE product_id = ? AND owner_id = ?");
$stmt->execute([$product_id, getUserId()]);
return (bool)$stmt->fetch();
} catch (PDOException $e) {
return false;
}
}
/**
* Check if the current user placed a specific order
* @param int $order_id Order ID to check
* @return bool True if user placed the order, false otherwise
*/
function placedOrder($order_id) {
if (!isCustomer()) return false;
global $pdo;
try {
$stmt = $pdo->prepare("SELECT order_id FROM orders WHERE order_id = ? AND customer_id = ?");
$stmt->execute([$order_id, getUserId()]);
return (bool)$stmt->fetch();
} catch (PDOException $e) {
return false;
}
}
/**
* Flash a message to display on the next page load
* @param string $key Message key
* @param string $message Message content
*/
function flashMessage($key, $message) {
$_SESSION['flash_messages'][$key] = $message;
}
/**
* Get and clear a flashed message
* @param string $key Message key
* @return string|null Message content if exists, null otherwise
*/
function getFlashMessage($key) {
if (isset($_SESSION['flash_messages'][$key])) {
$message = $_SESSION['flash_messages'][$key];
unset($_SESSION['flash_messages'][$key]);
return $message;
}
return null;
}
/**
* Initialize a user session after login
* @param array $user User data array (must contain user_id, username, and role)
*/
function initUserSession($user) {
$_SESSION['user_id'] = $user['user_id'];
$_SESSION['username'] = $user['username'];
$_SESSION['role'] = $user['role'];
// Regenerate session ID to prevent fixation
session_regenerate_id(true);
}
/**
* Destroy the current user session
*/
function destroySession() {
// Unset all session variables
$_SESSION = array();
// Delete session cookie
if (ini_get("session.use_cookies")) {
$params = session_get_cookie_params();
setcookie(
session_name(),
'',
time() - 42000,
$params["path"],
$params["domain"],
$params["secure"],
$params["httponly"]
);
}
// Destroy the session
session_destroy();
}
+53
View File
@@ -0,0 +1,53 @@
</main>
<footer style="background-color: var(--primary-dark); color: var(--light-text); padding: 40px 0; margin-top: 40px;">
<div class="container" style="background: transparent; box-shadow: none; backdrop-filter: none; -webkit-backdrop-filter: none; border: none; margin: 0 auto; padding: 0 30px;">
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 30px;">
<div>
<h3 style="margin-bottom: 15px; color: var(--light-text);">UniMarket</h3>
<p>Your university's premier marketplace for buying and selling products within the campus community.</p>
</div>
<div>
<h3 style="margin-bottom: 15px; color: var(--light-text);">Quick Links</h3>
<ul style="list-style: none;">
<li style="margin-bottom: 8px;"><a href="/unimarket/index.php" style="color: var(--light-text); text-decoration: none;">Home</a></li>
<?php if (!isLoggedIn()): ?>
<li style="margin-bottom: 8px;"><a href="/unimarket/auth/login.php" style="color: var(--light-text); text-decoration: none;">Login</a></li>
<li style="margin-bottom: 8px;"><a href="/unimarket/auth/register.php" style="color: var(--light-text); text-decoration: none;">Register</a></li>
<?php else: ?>
<?php if (isCustomer()): ?>
<li style="margin-bottom: 8px;"><a href="/unimarket/customer/products/browse.php" style="color: var(--light-text); text-decoration: none;">Browse Products</a></li>
<li style="margin-bottom: 8px;"><a href="/unimarket/customer/profile.php" style="color: var(--light-text); text-decoration: none;">My Profile</a></li>
<?php else: ?>
<li style="margin-bottom: 8px;"><a href="/unimarket/owner/dashboard.php" style="color: var(--light-text); text-decoration: none;">Dashboard</a></li>
<li style="margin-bottom: 8px;"><a href="/unimarket/owner/products/add.php" style="color: var(--light-text); text-decoration: none;">Add Product</a></li>
<?php endif; ?>
<?php endif; ?>
</ul>
</div>
<div>
<h3 style="margin-bottom: 15px; color: var(--light-text);">Contact</h3>
<ul style="list-style: none;">
<li style="margin-bottom: 8px;"><i class="fas fa-envelope" style="margin-right: 8px;"></i> contact@unimarket.edu</li>
<li style="margin-bottom: 8px;"><i class="fas fa-phone" style="margin-right: 8px;"></i> (123) 456-7890</li>
<li style="margin-bottom: 8px;"><i class="fas fa-map-marker-alt" style="margin-right: 8px;"></i> University Campus</li>
</ul>
</div>
</div>
<div style="border-top: 1px solid rgba(255,255,255,0.1); margin-top: 30px; padding-top: 20px; text-align: center;">
<p>&copy; <?php echo date('Y'); ?> UniMarket. All rights reserved.</p>
<div style="margin-top: 15px;">
<a href="#" style="color: var(--light-text); margin: 0 10px; font-size: 1.2rem;"><i class="fab fa-facebook"></i></a>
<a href="#" style="color: var(--light-text); margin: 0 10px; font-size: 1.2rem;"><i class="fab fa-twitter"></i></a>
<a href="#" style="color: var(--light-text); margin: 0 10px; font-size: 1.2rem;"><i class="fab fa-instagram"></i></a>
</div>
</div>
</div>
</footer>
<script src="/unimarket/assets/js/script.js"></script>
</body>
</html>
+794
View File
@@ -0,0 +1,794 @@
<?php
require_once __DIR__ . '/auth_functions.php';
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title><?php echo isset($page_title) ? $page_title . ' - ' : ''; ?>UniMarket - University Marketplace</title>
<link rel="stylesheet" href="/unimarket/assets/css/style.css">
<link rel="stylesheet" href="/unimarket/assets/css/responsive.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
<style>
.logo{
text-decoration: none;
color: var(--light-text);
display: flex;
align-items: center;
gap: 10px;
}
.logo img {
height: 30px;
width: auto;
filter: brightness(0) invert(1);
}
/* Sticky header styles */
header {
position: fixed;
top: 0;
left: 0;
right: 0;
z-index: 1000;
background: rgba(46, 125, 50, 0.95);
backdrop-filter: blur(10px);
-webkit-backdrop-filter: blur(10px);
max-width: 100%;
color: var(--light-text);
padding: 15px 0;
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
}
header .container {
background: transparent;
box-shadow: none;
backdrop-filter: none;
-webkit-backdrop-filter: none;
border: none;
margin: 0 auto;
padding: 0 30px;
}
body {
padding-top: 60px !important;
}
/* Adjust mobile menu for fixed header */
.mobile-menu {
position: fixed;
top: 60px;
left: 0;
right: 0;
z-index: 999;
background: rgba(46, 125, 50, 0.95);
backdrop-filter: blur(10px);
-webkit-backdrop-filter: blur(10px);
}
.header-content {
display: flex;
justify-content: space-between;
align-items: center;
}
nav ul {
display: flex;
list-style: none;
margin: 0;
padding: 0;
align-items: center;
gap: 10px;
}
nav ul li {
margin: 0;
}
nav ul li a {
color: var(--light-text);
text-decoration: none;
font-weight: 500;
padding: 8px 12px;
border-radius: 4px;
display: flex;
align-items: center;
gap: 8px;
transition: background-color 0.3s ease;
height: 40px;
white-space: nowrap;
}
nav ul li a:hover {
background-color: rgba(255, 255, 255, 0.1);
}
nav ul li a i {
width: 20px;
text-align: center;
font-size: 1rem;
}
.mobile-menu-toggle {
display: none;
color: var(--light-text);
font-size: 1.5rem;
cursor: pointer;
padding: 5px;
}
@media (max-width: 768px) {
.header-content nav ul {
display: none;
}
.mobile-menu-toggle {
display: block !important;
}
.mobile-menu {
display: none;
padding: 15px 0;
}
.mobile-menu ul {
list-style: none;
text-align: center;
padding: 0;
margin: 0;
}
.mobile-menu ul li {
margin: 0;
padding: 10px 0;
}
.mobile-menu ul li a {
display: block;
padding: 8px 15px;
color: var(--light-text);
text-decoration: none;
}
.mobile-menu ul li a:hover {
background: none;
}
}
/* Add logout modal styles */
.logout-modal {
display: none;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.5);
z-index: 2000;
justify-content: center;
align-items: center;
}
.logout-modal-content {
background: white;
padding: 30px;
border-radius: 8px;
text-align: center;
max-width: 400px;
width: 90%;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
}
.logout-modal h2 {
margin-bottom: 20px;
color: var(--primary-dark);
}
.logout-modal-buttons {
display: flex;
justify-content: center;
gap: 15px;
margin-top: 25px;
}
.logout-modal-buttons button {
padding: 10px 25px;
border: none;
border-radius: 4px;
cursor: pointer;
font-weight: 500;
transition: all 0.3s ease;
}
.logout-modal-buttons .btn-cancel {
background: #e0e0e0;
color: #333;
}
.logout-modal-buttons .btn-cancel:hover {
background: #d0d0d0;
}
.logout-modal-buttons .btn-confirm {
background: #ff4444;
color: white;
}
.logout-modal-buttons .btn-confirm:hover {
background: #ff0000;
}
/* Update notification styles */
.notification-btn {
position: relative;
color: var(--light-text);
text-decoration: none;
padding: 8px 12px;
border-radius: 4px;
cursor: pointer;
transition: all 0.3s ease;
display: flex;
align-items: center;
gap: 8px;
height: 40px;
white-space: nowrap;
}
.notification-btn:hover {
background-color: rgba(255, 255, 255, 0.1);
}
.notification-btn i {
width: 20px;
text-align: center;
font-size: 1rem;
}
.notification-badge {
position: absolute;
top: -5px;
right: -5px;
background-color: #ff4444;
color: white;
border-radius: 50%;
padding: 2px 6px;
font-size: 0.7rem;
display: none;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
animation: pulse 2s infinite;
}
.notification-badge.show {
display: block;
}
.notification-container {
position: relative;
}
.notification-modal {
display: none;
position: absolute;
top: calc(100% + 10px);
right: 0;
width: 350px;
max-height: 80vh;
background: white;
border-radius: 8px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
z-index: 2000;
animation: slideDown 0.2s ease;
overflow: hidden;
}
@keyframes slideDown {
from {
transform: translateY(-10px);
opacity: 0;
}
to {
transform: translateY(0);
opacity: 1;
}
}
.notification-modal::before {
content: '';
position: absolute;
top: -8px;
right: 20px;
width: 16px;
height: 16px;
background: white;
transform: rotate(45deg);
box-shadow: -2px -2px 5px rgba(0, 0, 0, 0.05);
}
.notification-modal-content {
background: white;
padding: 0;
border-radius: 8px;
max-height: 80vh;
overflow: hidden;
}
.notification-modal-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 15px;
background: var(--primary-color);
color: white;
border-bottom: 1px solid rgba(0, 0, 0, 0.1);
}
.notification-modal-header h2 {
margin: 0;
font-size: 1.1rem;
font-weight: 600;
display: flex;
align-items: center;
gap: 8px;
}
.notification-modal-close {
background: none;
border: none;
font-size: 1.2rem;
cursor: pointer;
color: white;
opacity: 0.8;
transition: opacity 0.3s;
padding: 5px;
line-height: 1;
}
.notification-modal-close:hover {
opacity: 1;
}
.notification-list {
list-style: none;
padding: 0;
margin: 0;
max-height: 400px;
overflow-y: auto;
}
.notification-list::-webkit-scrollbar {
width: 4px;
}
.notification-list::-webkit-scrollbar-track {
background: #f1f1f1;
}
.notification-list::-webkit-scrollbar-thumb {
background: #888;
border-radius: 2px;
}
.notification-list::-webkit-scrollbar-thumb:hover {
background: #666;
}
.notification-item {
padding: 12px 15px;
border-bottom: 1px solid #eee;
display: flex;
align-items: flex-start;
gap: 10px;
transition: background-color 0.2s;
}
.notification-item:hover {
background-color: #f8f9fa;
}
.notification-item.unread {
background-color: #f0f7ff;
}
.notification-icon {
width: 32px;
height: 32px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
}
.notification-icon.ready {
background-color: #d4edda;
color: #155724;
}
.notification-icon.completed {
background-color: #e2e3e5;
color: #383d41;
}
.notification-icon.cancelled {
background-color: #f8d7da;
color: #721c24;
}
.notification-content {
flex: 1;
min-width: 0;
}
.notification-title {
font-weight: 500;
margin-bottom: 4px;
color: #333;
font-size: 0.9rem;
line-height: 1.4;
}
.notification-time {
font-size: 0.8rem;
color: #666;
}
.notification-status {
display: inline-block;
padding: 2px 6px;
border-radius: 10px;
font-size: 0.75rem;
font-weight: 500;
margin-top: 4px;
}
.status-ready {
background-color: #d4edda;
color: #155724;
}
.status-completed {
background-color: #e2e3e5;
color: #383d41;
}
.status-cancelled {
background-color: #f8d7da;
color: #721c24;
}
.no-notifications {
text-align: center;
padding: 30px 20px;
color: #666;
background: #f8f9fa;
}
.no-notifications i {
font-size: 1.8rem;
color: #ccc;
margin-bottom: 8px;
display: block;
}
.no-notifications p {
margin: 0;
font-size: 0.85rem;
}
@media (max-width: 768px) {
.notification-modal {
position: fixed;
top: 60px;
right: 0;
left: 0;
width: 100%;
max-height: calc(100vh - 60px);
margin: 0;
border-radius: 0;
}
.notification-modal::before {
display: none;
}
}
.logout-link {
color: var(--light-text) !important;
transition: all 0.3s ease !important;
background: none !important;
background-color: transparent !important;
display: flex !important;
align-items: center !important;
height: 40px !important;
white-space: nowrap !important;
}
.logout-link:hover {
background-color: rgba(255, 68, 68, 0.1) !important;
color: #ff4444 !important;
}
.logout-link i {
width: 20px !important;
text-align: center !important;
font-size: 1rem !important;
}
</style>
</head>
<body>
<!-- Add logout modal HTML -->
<div class="logout-modal" id="logoutModal">
<div class="logout-modal-content">
<h2>Confirm Logout</h2>
<p>Are you sure you want to logout?</p>
<div class="logout-modal-buttons">
<button class="btn-cancel" onclick="closeLogoutModal()">Cancel</button>
<button class="btn-confirm" onclick="confirmLogout()">Logout</button>
</div>
</div>
</div>
<header>
<div class="container header-content">
<a href="/unimarket/index.php" class="logo">
<img src="/unimarket/assets/images/logo/logo.png" alt="UniMarket Logo">
</a>
<nav>
<ul>
<?php if (isLoggedIn()): ?>
<?php if (isCustomer()): ?>
<li><a href="/unimarket/customer/products/browse.php"><i class="fas fa-search"></i> Browse</a></li>
<li><a href="/unimarket/customer/cart.php"><i class="fas fa-shopping-cart"></i> Cart</a></li>
<li class="notification-container">
<a href="#" onclick="openNotificationModal(); return false;" class="notification-btn">
<i class="fas fa-bell"></i> Notifications
<span class="notification-badge" id="notificationBadge"></span>
</a>
<div class="notification-modal" id="notificationModal">
<div class="notification-modal-content">
<div class="notification-modal-header">
<h2><i class="fas fa-bell"></i> Notifications</h2>
<button class="notification-modal-close" onclick="closeNotificationModal()">&times;</button>
</div>
<div class="notification-list" id="notificationList">
<div class="no-notifications">
<i class="fas fa-bell-slash"></i>
<p>No notifications yet</p>
</div>
</div>
</div>
</div>
</li>
<li><a href="/unimarket/customer/profile.php"><i class="fas fa-user"></i> Profile</a></li>
<?php elseif (isAdmin()): ?>
<li><a href="/unimarket/admin/dashboard.php"><i class="fas fa-tachometer-alt"></i> Admin Dashboard</a></li>
<li><a href="/unimarket/admin/users/manage.php"><i class="fas fa-users"></i> Users</a></li>
<li><a href="/unimarket/admin/products/index.php"><i class="fas fa-box"></i> Products</a></li>
<li><a href="/unimarket/admin/orders/manage.php"><i class="fas fa-shopping-cart"></i> Orders</a></li>
<?php else: ?>
<li><a href="/unimarket/owner/dashboard.php"><i class="fas fa-tachometer-alt"></i> Dashboard</a></li>
<li><a href="/unimarket/owner/products/list.php"><i class="fas fa-box-open"></i> Products</a></li>
<?php endif; ?>
<li><a href="#" onclick="openLogoutModal(); return false;" class="logout-link"><i class="fas fa-sign-out-alt"></i> Logout</a></li>
<?php else: ?>
<li><a href="/unimarket/index.php"><i class="fas fa-home"></i> Home</a></li>
<li><a href="/unimarket/auth/login.php"><i class="fas fa-sign-in-alt"></i> Login</a></li>
<li><a href="/unimarket/auth/register.php"><i class="fas fa-user-plus"></i> Register</a></li>
<?php endif; ?>
</ul>
</nav>
<?php if (isLoggedIn()): ?>
<div class="mobile-menu-toggle" style="display: none;">
<i class="fas fa-bars"></i>
</div>
<?php endif; ?>
</div>
</header>
<?php if (isLoggedIn()): ?>
<style>
.mobile-menu {
display: none;
background-color: var(--primary-dark);
padding: 15px 0;
}
.mobile-menu ul {
list-style: none;
text-align: center;
}
.mobile-menu ul li {
margin-bottom: 10px;
}
.mobile-menu ul li a {
color: var(--light-text);
text-decoration: none;
display: block;
padding: 8px 0;
}
.mobile-menu-toggle {
color: var(--light-text);
font-size: 1.5rem;
cursor: pointer;
}
@media (max-width: 768px) {
.header-content nav ul {
display: none;
}
.mobile-menu-toggle {
display: block !important;
}
}
</style>
<div class="mobile-menu" id="mobileMenu">
<div class="container">
<ul>
<?php if (isCustomer()): ?>
<li><a href="/unimarket/customer/products/browse.php"><i class="fas fa-search"></i> Browse</a></li>
<li><a href="/unimarket/customer/cart.php"><i class="fas fa-shopping-cart"></i> Cart</a></li>
<li class="notification-container">
<a href="#" onclick="openNotificationModal(); return false;" class="notification-btn">
<i class="fas fa-bell"></i> Notifications
<span class="notification-badge" id="notificationBadge"></span>
</a>
<div class="notification-modal" id="notificationModal">
<div class="notification-modal-content">
<div class="notification-modal-header">
<h2><i class="fas fa-bell"></i> Notifications</h2>
<button class="notification-modal-close" onclick="closeNotificationModal()">&times;</button>
</div>
<div class="notification-list" id="notificationList">
<div class="no-notifications">
<i class="fas fa-bell-slash"></i>
<p>No notifications yet</p>
</div>
</div>
</div>
</div>
</li>
<li><a href="/unimarket/customer/profile.php"><i class="fas fa-user"></i> Profile</a></li>
<?php elseif (isAdmin()): ?>
<li><a href="/unimarket/admin/dashboard.php"><i class="fas fa-tachometer-alt"></i> Admin Dashboard</a></li>
<li><a href="/unimarket/admin/users/manage.php"><i class="fas fa-users"></i> Users</a></li>
<li><a href="/unimarket/admin/products/index.php"><i class="fas fa-box"></i> Products</a></li>
<li><a href="/unimarket/admin/orders/manage.php"><i class="fas fa-shopping-cart"></i> Orders</a></li>
<?php else: ?>
<li><a href="/unimarket/owner/dashboard.php"><i class="fas fa-tachometer-alt"></i> Dashboard</a></li>
<li><a href="/unimarket/owner/products/list.php"><i class="fas fa-box-open"></i> Products</a></li>
<?php endif; ?>
<li><a href="#" onclick="openLogoutModal(); return false;" class="logout-link"><i class="fas fa-sign-out-alt"></i> Logout</a></li>
</ul>
</div>
</div>
<script>
document.querySelector('.mobile-menu-toggle').addEventListener('click', function() {
const menu = document.getElementById('mobileMenu');
menu.style.display = menu.style.display === 'block' ? 'none' : 'block';
});
// Add logout modal functions
function openLogoutModal() {
document.getElementById('logoutModal').style.display = 'flex';
}
function closeLogoutModal() {
document.getElementById('logoutModal').style.display = 'none';
}
function confirmLogout() {
window.location.href = '/unimarket/auth/logout.php';
}
// Close modal when clicking outside
document.getElementById('logoutModal').addEventListener('click', function(e) {
if (e.target === this) {
closeLogoutModal();
}
});
</script>
<?php endif; ?>
<main class="container">
<script>
document.addEventListener('DOMContentLoaded', function() {
// Remove the scroll event listener that was causing the hover effect
});
</script>
<script>
// Notification Modal Functions
function openNotificationModal() {
const modal = document.getElementById('notificationModal');
modal.style.display = 'block';
loadNotifications();
// Close modal when clicking outside
document.addEventListener('click', function closeModal(e) {
if (!modal.contains(e.target) && !e.target.closest('.notification-btn')) {
modal.style.display = 'none';
document.removeEventListener('click', closeModal);
}
});
}
function closeNotificationModal() {
document.getElementById('notificationModal').style.display = 'none';
}
function loadNotifications() {
const notificationList = document.getElementById('notificationList');
const badge = document.getElementById('notificationBadge');
fetch('/unimarket/customer/notifications.php')
.then(response => {
if (!response.ok) {
throw new Error('Network response was not ok');
}
return response.json();
})
.then(data => {
if (data.error) {
throw new Error(data.error);
}
if (data.notifications.length === 0) {
notificationList.innerHTML = `
<div class="no-notifications">
<i class="fas fa-bell-slash"></i>
<p>No notifications yet</p>
</div>
`;
} else {
notificationList.innerHTML = data.notifications.map(notification => `
<div class="notification-item ${notification.unread ? 'unread' : ''}">
<div class="notification-icon ${notification.status}">
<i class="fas ${notification.status === 'ready' ? 'fa-check-circle' :
notification.status === 'completed' ? 'fa-check-double' :
'fa-times-circle'}"></i>
</div>
<div class="notification-content">
<div class="notification-title">${notification.title}</div>
<div class="notification-time">${notification.time}</div>
<span class="notification-status status-${notification.status}">
${notification.status.charAt(0).toUpperCase() + notification.status.slice(1)}
</span>
</div>
</div>
`).join('');
}
// Update badge
if (data.unread_count > 0) {
badge.textContent = data.unread_count;
badge.classList.add('show');
} else {
badge.classList.remove('show');
}
})
.catch(error => {
console.error('Error:', error);
notificationList.innerHTML = `
<div class="no-notifications">
<i class="fas fa-exclamation-circle"></i>
<p>Error loading notifications: ${error.message}</p>
<p>Please try again later</p>
</div>
`;
});
}
// Load notifications every 30 seconds if user is logged in
<?php if (isLoggedIn()): ?>
setInterval(loadNotifications, 30000);
<?php endif; ?>
</script>
+20
View File
@@ -0,0 +1,20 @@
<li class="nav-item">
<a class="nav-link" href="<?php echo BASE_URL; ?>owner/products/list.php">
<i class="fas fa-box me-2"></i>Products
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="<?php echo BASE_URL; ?>owner/orders/list.php">
<i class="fas fa-shopping-cart me-2"></i>Orders
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="<?php echo BASE_URL; ?>owner/reports.php">
<i class="fas fa-chart-bar me-2"></i>Reports
</a>
</li>
</ul>
</div>
</div>
</div>
</div>
+248
View File
@@ -0,0 +1,248 @@
<?php
require_once 'includes/auth_functions.php';
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>UniMarket - University Marketplace</title>
<link rel="stylesheet" href="/unimarket/assets/css/style.css">
<link rel="stylesheet" href="/unimarket/assets/css/responsive.css">
<style>
header {
position: fixed;
top: 0;
left: 0;
right: 0;
z-index: 1000;
background: rgba(46, 125, 50, 0.95);
backdrop-filter: blur(10px);
-webkit-backdrop-filter: blur(10px);
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
}
.hero {
background: linear-gradient(rgba(0, 0, 0, 0.5), rgba(0, 0, 0, 0.5)), url('/unimarket/assets/images/hero/hero-bg.jpg');
background-size: cover;
background-position: center;
background-repeat: no-repeat;
color: var(--light-text);
padding: 120px 0;
text-align: center;
margin-bottom: 40px;
position: relative;
}
.hero .container {
position: relative;
z-index: 2;
background: transparent;
box-shadow: none;
backdrop-filter: none;
-webkit-backdrop-filter: none;
border: none;
}
.hero h1 {
font-size: 3.5rem;
margin-bottom: 20px;
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.3);
}
.hero p {
font-size: 1.3rem;
max-width: 800px;
margin: 0 auto 30px;
text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.3);
}
.hero .btn {
padding: 12px 30px;
font-size: 1.1rem;
margin: 0 10px;
transition: all 0.3s ease;
background-color: transparent;
border: 2px solid var(--light-text);
color: var(--light-text);
}
.hero .btn:hover {
background-color: var(--light-text);
color: var(--primary-color);
transform: translateY(-2px);
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
}
.hero .btn-secondary {
background-color: transparent;
border: 2px solid var(--light-text);
}
.hero .btn-secondary:hover {
background-color: var(--light-text);
color: var(--primary-color);
}
.features {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 30px;
margin: 40px 0;
}
.feature-card {
background-color: #fff;
border-radius: 8px;
padding: 30px;
text-align: center;
box-shadow: 0 5px 15px rgba(0,0,0,0.05);
transition: transform 0.3s;
}
.feature-card:hover {
transform: translateY(-10px);
}
.feature-icon {
font-size: 3rem;
color: var(--primary-color);
margin-bottom: 20px;
}
.feature-card h3 {
margin-bottom: 15px;
color: var(--primary-dark);
}
.cta-section {
background-color: #f5f5f5;
padding: 60px 0;
text-align: center;
margin-top: 40px;
}
.trending-products {
margin: 60px 0;
}
.section-title {
text-align: center;
margin-bottom: 40px;
color: var(--primary-dark);
}
</style>
</head>
<body>
<?php include 'includes/header.php'; ?>
<div class="hero">
<div class="container">
<h1>Welcome to UniMarket</h1>
<p>The University Marketplace for Affordable Student Essentials, Books, Supplies & Campus Merchandise.</p>
<?php if (!isLoggedIn()): ?>
<div>
<a href="/unimarket/auth/register.php" class="btn" style="margin-right: 10px;">Join Now</a>
<a href="/unimarket/auth/login.php" class="btn btn-secondary">Sign In</a>
</div>
<?php else: ?>
<div>
<?php if (isCustomer()): ?>
<a href="/unimarket/customer/products/browse.php" class="btn">Browse Products</a>
<?php else: ?>
<a href="/unimarket/owner/dashboard.php" class="btn">Go to Dashboard</a>
<?php endif; ?>
</div>
<?php endif; ?>
</div>
</div>
<div class="container">
<div class="features">
<div class="feature-card">
<div class="feature-icon">🛍️</div>
<h3>Campus Marketplace</h3>
<p>Buy and sell products exclusively within your university community.</p>
</div>
<div class="feature-card">
<div class="feature-icon">🔄</div>
<h3>Easy Pickup</h3>
<p>Schedule convenient on-campus pickup times for your orders.</p>
</div>
<div class="feature-card">
<div class="feature-icon">🛒</div>
<h3>Secure Transactions</h3>
<p>Safe and reliable transactions between students and campus vendors.</p>
</div>
</div>
<?php
// Display trending products (only for customers or guests)
if (!isLoggedIn() || isCustomer()) {
require_once 'config/database.php';
$stmt = $pdo->query("SELECT * FROM products ORDER BY created_at DESC LIMIT 6");
$trending_products = $stmt->fetchAll();
if (!empty($trending_products)) {
echo '<div class="trending-products">';
echo '<h2 class="section-title">Trending Products</h2>';
echo '<div class="product-grid">';
foreach ($trending_products as $product) {
echo '<div class="product-card"">';
echo '<div class="product-image">';
if ($product['image_path']) {
echo '<img src="/unimarket/uploads/products/'.$product['image_path'].'" alt="'.htmlspecialchars($product['name']).'">';
} else {
echo '<div style="display: flex; align-items: center; justify-content: center; height: 100%; color: #999;">No Image</div>';
}
echo '</div>';
echo '<div class="product-info">';
echo '<h3 class="product-title">'.htmlspecialchars($product['name']).'</h3>';
echo '<p class="product-price">₱'.number_format($product['price'], 2).'</p>';
if (isLoggedIn()) {
echo '<a href="/unimarket/customer/products/view.php?id='.$product['product_id'].'" class="btn" style="display: block; text-align: center;">';
echo '<i class="fas fa-eye"></i> View Details';
echo '</a>';
} else {
echo '<a href="/unimarket/auth/login.php" class="btn" style="display: block; text-align: center;">Login to Purchase</a>';
}
echo '</div>';
echo '</div>';
}
echo '</div>';
echo '<div style="text-align: center; margin-top: 30px;">';
echo '<a href="/unimarket/customer/products/browse.php" class="btn">View All Products</a>';
echo '</div>';
echo '</div>';
}
}
?>
</div>
<div class="cta-section">
<div class="container">
<h2>Ready to join the campus marketplace?</h2>
<p style="max-width: 600px; margin: 0 auto 30px;">Whether you're looking to buy or sell, UniMarket connects you with your university community.</p>
<?php if (!isLoggedIn()): ?>
<a href="/unimarket/auth/register.php" class="btn">Get Started</a>
<?php else: ?>
<?php if (isCustomer()): ?>
<a href="/unimarket/customer/products/browse.php" class="btn">Browse Products</a>
<?php else: ?>
<a href="/unimarket/owner/products/add.php" class="btn">Add New Product</a>
<?php endif; ?>
<?php endif; ?>
</div>
</div>
<?php include 'includes/footer.php'; ?>
</body>
</html>
+566
View File
@@ -0,0 +1,566 @@
<?php
require_once '../includes/auth_functions.php';
redirectIfNotLoggedIn();
redirectIfNotOwner();
require_once '../config/database.php';
// Get owner's products count
$stmt = $pdo->prepare("SELECT COUNT(*) FROM products WHERE owner_id = ?");
$stmt->execute([$_SESSION['user_id']]);
$products_count = $stmt->fetchColumn();
// Get pending orders count
$stmt = $pdo->prepare("SELECT COUNT(*) FROM orders o
JOIN order_items oi ON o.order_id = oi.order_id
JOIN products p ON oi.product_id = p.product_id
WHERE p.owner_id = ? AND o.status = 'pending'");
$stmt->execute([$_SESSION['user_id']]);
$pending_orders_count = $stmt->fetchColumn();
// Get recent orders
$stmt = $pdo->prepare("SELECT o.order_id, o.order_date, o.total_amount, o.status, u.username
FROM orders o
JOIN order_items oi ON o.order_id = oi.order_id
JOIN products p ON oi.product_id = p.product_id
JOIN users u ON o.customer_id = u.user_id
WHERE p.owner_id = ?
GROUP BY o.order_id
ORDER BY o.order_date DESC
LIMIT 5");
$stmt->execute([$_SESSION['user_id']]);
$recent_orders = $stmt->fetchAll();
// Get vendor information
$vendor_stmt = $pdo->prepare("SELECT * FROM users WHERE user_id = ? AND role = 'owner'");
$vendor_stmt->execute([$_SESSION['user_id']]);
$vendor = $vendor_stmt->fetch();
// Get total products count
$products_stmt = $pdo->prepare("SELECT COUNT(*) FROM products WHERE owner_id = ?");
$products_stmt->execute([$_SESSION['user_id']]);
$total_products = $products_stmt->fetchColumn();
// Get total orders count
$orders_stmt = $pdo->prepare("
SELECT COUNT(*)
FROM orders o
JOIN order_items oi ON o.order_id = oi.order_id
JOIN products p ON oi.product_id = p.product_id
WHERE p.owner_id = ?
");
$orders_stmt->execute([$_SESSION['user_id']]);
$total_orders = $orders_stmt->fetchColumn();
// Get total revenue
$revenue_stmt = $pdo->prepare("
SELECT COALESCE(SUM(oi.quantity * oi.price), 0) as total_revenue
FROM orders o
JOIN order_items oi ON o.order_id = oi.order_id
JOIN products p ON oi.product_id = p.product_id
WHERE p.owner_id = ? AND o.status = 'completed'
");
$revenue_stmt->execute([$_SESSION['user_id']]);
$total_revenue = $revenue_stmt->fetchColumn();
// Get recent orders
$recent_orders_stmt = $pdo->prepare("
SELECT o.order_id, o.order_date, o.status, o.total_amount,
u.first_name, u.last_name
FROM orders o
JOIN order_items oi ON o.order_id = oi.order_id
JOIN products p ON oi.product_id = p.product_id
JOIN users u ON o.customer_id = u.user_id
WHERE p.owner_id = ?
GROUP BY o.order_id
ORDER BY o.order_date DESC
LIMIT 4
");
$recent_orders_stmt->execute([$_SESSION['user_id']]);
$recent_orders = $recent_orders_stmt->fetchAll();
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Owner Dashboard - UniMarket</title>
<link rel="stylesheet" href="/unimarket/assets/css/style.css">
<link rel="stylesheet" href="/unimarket/assets/css/responsive.css">
<style>
/* Sidebar Styles */
.sidebar {
width: 250px;
background: #ffffff;
color: #333;
padding: 20px 0;
height: 100vh;
position: fixed;
left: 0;
top: 0;
box-shadow: 2px 0 10px rgba(0,0,0,0.1);
z-index: 1000;
}
.sidebar-header {
padding: 0 20px 20px;
border-bottom: 1px solid #eee;
margin-bottom: 20px;
}
.sidebar-header h2 {
color: #333;
font-size: 1.5rem;
margin: 0;
padding: 0;
font-weight: 600;
}
.sidebar-header p {
color: #666;
margin: 5px 0 0;
font-size: 0.9rem;
}
.nav-menu {
list-style: none;
padding: 0;
margin: 0;
}
.nav-item {
margin: 5px 0;
}
.nav-link {
display: flex;
align-items: center;
padding: 12px 20px;
color: #333;
text-decoration: none;
transition: all 0.3s ease;
border-left: 3px solid transparent;
}
.nav-link:hover {
background: #e8f5e9;
color: #2e7d32;
border-left-color: #2e7d32;
}
.nav-link.active {
background: #e8f5e9;
color: #2e7d32;
border-left-color: #2e7d32;
font-weight: 500;
}
.nav-link i {
width: 20px;
margin-right: 10px;
font-size: 1.1rem;
}
.nav-link span {
font-size: 0.95rem;
}
.logout-link {
margin-top: auto;
border-top: 1px solid #eee;
padding-top: 20px;
}
.logout-link a {
color: var(--light-text);
display: flex;
align-items: center;
padding: 12px 20px;
text-decoration: none;
transition: all 0.3s ease;
height: 40px;
white-space: nowrap;
background-color: transparent !important;
}
.logout-link a:hover {
background: rgba(255, 68, 68, 0.1);
color: #ff4444;
}
.logout-link i {
width: 20px;
text-align: center;
font-size: 1rem;
margin-right: 10px;
}
/* Main Content Styles */
.main-content {
margin-left: 250px;
padding: 40px;
min-height: 100vh;
background: #f5f6fa;
width: calc(100% - 250px);
}
.dashboard-container {
max-width: 1800px;
margin: 0 auto;
padding: 30px;
}
.dashboard-header {
margin-bottom: 40px;
padding: 20px 0;
border-bottom: 2px solid #f0f0f0;
position: relative;
}
.dashboard-header h1 {
font-size: 2rem;
color: #2e7d32;
margin: 0;
font-weight: 600;
display: flex;
align-items: center;
gap: 12px;
}
.dashboard-header h1::before {
content: '';
display: inline-block;
width: 4px;
height: 24px;
background-color: #2e7d32;
border-radius: 2px;
}
.dashboard-header p {
margin: 8px 0 0 0;
color: #666;
font-size: 1rem;
}
.stats-grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 20px;
margin-bottom: 50px;
max-width: 1200px;
}
.stat-card {
background: white;
padding: 25px;
border-radius: 12px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
text-align: center;
transition: transform 0.2s ease;
aspect-ratio: 1;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.stat-card:hover {
transform: translateY(-5px);
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
}
.stat-card h3 {
margin: 0 0 15px 0;
color: #666;
font-size: 1.1rem;
}
.stat-card .value {
font-size: 2.5rem;
font-weight: bold;
color: #2e7d32;
}
.recent-orders {
background: white;
padding: 25px 40px;
border-radius: 12px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
width: fit-content;
margin: 0 auto;
}
.recent-orders h2 {
margin: 0 0 20px 0;
color: #333;
font-size: 1.4rem;
font-weight: 600;
padding-bottom: 12px;
border-bottom: 2px solid #f0f0f0;
}
.view-all-btn {
display: inline-flex;
align-items: center;
gap: 6px;
padding: 6px 12px;
background-color: #2e7d32;
color: white;
text-decoration: none;
border-radius: 4px;
font-size: 0.85rem;
font-weight: 500;
transition: all 0.2s ease;
margin-top: 20px;
}
.view-all-btn:hover {
background-color: #1b5e20;
transform: translateY(-1px);
}
.table {
width: 100%;
border-collapse: separate;
border-spacing: 0;
margin-top: 10px;
min-width: 600px;
}
.table th,
.table td {
padding: 16px 20px;
vertical-align: middle;
border-bottom: 1px solid #f0f0f0;
font-size: 0.95rem;
}
.table th {
background-color: #f8f9fa;
font-weight: 600;
color: #555;
text-transform: uppercase;
font-size: 0.85rem;
letter-spacing: 0.5px;
white-space: nowrap;
border-top: 1px solid #f0f0f0;
min-width: 100px;
}
.table th:first-child {
border-top-left-radius: 8px;
min-width: 80px;
}
.table th:nth-child(2) {
min-width: 150px;
}
.table th:nth-child(3) {
min-width: 100px;
}
.table th:last-child {
border-top-right-radius: 8px;
min-width: 120px;
}
.table td {
white-space: nowrap;
color: #444;
}
.table tr:hover {
background-color: #f8f9fa;
}
.table tr:last-child td {
border-bottom: none;
}
.status-badge {
display: inline-block;
padding: 6px 12px;
font-size: 0.85rem;
font-weight: 500;
line-height: 1;
text-align: center;
white-space: nowrap;
vertical-align: baseline;
border-radius: 6px;
background-color: #e9ecef;
color: #495057;
}
.status-badge.pending {
background-color: #fff3cd;
color: #856404;
}
.status-badge.processing {
background-color: #cce5ff;
color: #004085;
}
.status-badge.ready {
background-color: #d4edda;
color: #155724;
}
.status-badge.completed {
background-color: #e2e3e5;
color: #383d41;
}
.status-badge.cancelled {
background-color: #f8d7da;
color: #721c24;
}
#order-number {
color: #2e7d32;
text-decoration: none;
font-weight: 500;
font-size: 0.95rem;
transition: color 0.2s ease;
}
#order-number:hover {
color: #1b5e20;
text-decoration: none;
}
@media (max-width: 1600px) {
.stats-grid {
grid-template-columns: repeat(2, 1fr);
}
}
@media (max-width: 768px) {
.stats-grid {
grid-template-columns: 1fr;
}
.recent-orders {
margin: 0 -20px;
border-radius: 0;
padding: 20px;
}
.table {
display: block;
overflow-x: auto;
}
}
</style>
</head>
<body>
<?php include '../includes/header.php'; ?>
<div class="container dashboard-container">
<div class="sidebar">
<div class="sidebar-header">
<h2>Vendor Dashboard</h2>
<p>Welcome, <?php echo htmlspecialchars($vendor['first_name'] . ' ' . $vendor['last_name']); ?></p>
</div>
<ul class="nav-menu">
<li class="nav-item">
<a href="dashboard.php" class="nav-link active">
<i class="fas fa-chart-line"></i>
<span>Dashboard</span>
</a>
</li>
<li class="nav-item">
<a href="products/list.php" class="nav-link">
<i class="fas fa-box"></i>
<span>Products</span>
</a>
</li>
<li class="nav-item">
<a href="orders/list.php" class="nav-link">
<i class="fas fa-shopping-cart"></i>
<span>Orders</span>
</a>
</li>
<li class="nav-item">
<a href="reports.php" class="nav-link">
<i class="fas fa-chart-bar"></i>
<span>Reports</span>
</a>
</li>
</ul>
<div class="logout-link">
<a href="/unimarket/auth/logout.php">
<i class="fas fa-sign-out-alt"></i>
<span>Logout</span>
</a>
</div>
</div>
<div class="main-content">
<div class="dashboard-container">
<div class="dashboard-header">
<h1>Dashboard Overview</h1>
<p>Welcome to your vendor dashboard</p>
</div>
<div class="stats-grid">
<div class="stat-card">
<h3>Total Products</h3>
<div class="value"><?php echo $total_products; ?></div>
</div>
<div class="stat-card">
<h3>Total Orders</h3>
<div class="value"><?php echo $total_orders; ?></div>
</div>
</div>
<div class="recent-orders">
<h2>Recent Orders</h2>
<table class="table">
<thead>
<tr>
<th>Order ID</th>
<th>Customer</th>
<th>Amount</th>
<th>Status</th>
</tr>
</thead>
<tbody>
<?php foreach ($recent_orders as $order): ?>
<tr>
<td>
<a href="/unimarket/owner/orders/view.php?order_id=<?php echo $order['order_id']; ?>" id="order-number">
#<?php echo $order['order_id']; ?>
</a>
</td>
<td><?php echo htmlspecialchars($order['first_name'] . ' ' . $order['last_name']); ?></td>
<td>₱<?php echo number_format($order['total_amount'], 2); ?></td>
<td>
<span class="status-badge <?php echo strtolower(str_replace('_', '', $order['status'])); ?>">
<?php echo ucfirst(str_replace('_', ' ', $order['status'])); ?>
</span>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<a href="/unimarket/owner/orders/list.php" class="view-all-btn">
<i class="fas fa-list"></i>
View All Orders
</a>
</div>
</div>
</div>
</div>
<script>
// Remove modal-related JavaScript
</script>
<?php include '../includes/footer.php'; ?>
</body>
</html>
+10
View File
@@ -0,0 +1,10 @@
<div class="container-fluid">
<div class="row">
<div class="col-12">
<div class="d-flex justify-content-between align-items-center mb-4">
<h1 class="h3 mb-0">Manage Orders</h1>
<a href="dashboard.php" class="btn btn-outline-secondary">
<i class="fas fa-arrow-left me-2"></i>Back to Dashboard
</a>
</div>
<div class="card">
+301
View File
@@ -0,0 +1,301 @@
<?php
require_once '../../includes/auth_functions.php';
redirectIfNotLoggedIn();
redirectIfNotOwner();
require_once '../../config/database.php';
// Debug query to check order items with sizes
$debug_stmt = $pdo->prepare("
SELECT oi.order_id, oi.product_id, oi.size, p.name, p.category
FROM order_items oi
JOIN products p ON oi.product_id = p.product_id
WHERE p.owner_id = ?
ORDER BY oi.order_id DESC
LIMIT 5
");
$debug_stmt->execute([$_SESSION['user_id']]);
$debug_items = $debug_stmt->fetchAll();
error_log("Recent order items with sizes: " . print_r($debug_items, true));
// Get all orders with customer info for products owned by this owner
$stmt = $pdo->prepare("
SELECT o.order_id, o.order_date, o.total_amount, o.status, o.pickup_schedule,
u.username AS customer_username, u.first_name, u.last_name,
GROUP_CONCAT(
CONCAT(
p.name,
CASE
WHEN p.category = 'clothes' AND oi.size IS NOT NULL AND oi.size != ''
THEN CONCAT(' (Size: ', oi.size, ')')
ELSE ''
END,
' x', oi.quantity
) ORDER BY p.name SEPARATOR ', '
) as order_items,
COUNT(DISTINCT oi.product_id) as item_count
FROM orders o
JOIN order_items oi ON o.order_id = oi.order_id
JOIN products p ON oi.product_id = p.product_id
JOIN users u ON o.customer_id = u.user_id
WHERE p.owner_id = ?
GROUP BY o.order_id, o.order_date, o.total_amount, o.status, o.pickup_schedule,
u.username, u.first_name, u.last_name
ORDER BY o.order_date DESC
");
$stmt->execute([$_SESSION['user_id']]);
$orders = $stmt->fetchAll();
// For debugging
error_log("Orders query result: " . print_r($orders, true));
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Manage Orders - UniMarket</title>
<link rel="stylesheet" href="/unimarket/assets/css/style.css">
<link rel="stylesheet" href="/unimarket/assets/css/responsive.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
<style>
.orders-container {
margin-top: 30px;
}
.orders-table {
width: 100%;
border-collapse: collapse;
margin-top: 20px;
}
.orders-table th, .orders-table td {
padding: 12px 15px;
text-align: left;
border-bottom: 1px solid #eee;
}
.orders-table th {
background-color: #f5f5f5;
font-weight: 600;
}
.order-items {
max-width: 300px;
}
.order-item {
padding: 8px 0;
border-bottom: 1px solid #f0f0f0;
display: flex;
flex-direction: column;
gap: 4px;
}
.order-item:last-child {
border-bottom: none;
}
.item-name {
font-weight: 500;
}
.item-size {
color: #666;
font-size: 0.9em;
background-color: #f8f9fa;
padding: 2px 6px;
border-radius: 4px;
display: inline-block;
}
.item-quantity {
color: #666;
font-size: 0.9em;
}
.order-status {
display: inline-block;
padding: 5px 10px;
border-radius: 4px;
font-size: 0.9rem;
font-weight: 500;
}
.status-pending {
background-color: #fff3cd;
color: #856404;
}
.status-processing {
background-color: #cce5ff;
color: #004085;
}
.status-ready {
background-color: #d4edda;
color: #155724;
}
.status-completed {
background-color: #e2e3e5;
color: #383d41;
}
.status-cancelled {
background-color: #f8d7da;
color: #721c24;
}
.action-link {
color: var(--primary-color);
text-decoration: none;
margin-right: 10px;
}
.action-link:hover {
text-decoration: underline;
}
.empty-orders {
text-align: center;
padding: 50px;
background-color: #f9f9f9;
border-radius: 8px;
}
@media (max-width: 768px) {
.orders-table {
display: block;
overflow-x: auto;
}
}
</style>
</head>
<body>
<?php include '../../includes/header.php'; ?>
<div class="container-fluid">
<div class="row">
<div class="col-12">
<div class="d-flex justify-content-between align-items-center mb-4">
<h1 class="h3 mb-0">Manage Orders</h1>
<a href="../dashboard.php" class="btn btn-outline-secondary">
<i class="fas fa-arrow-left me-2"></i>Back to Dashboard
</a>
</div>
<div class="card">
<div class="container orders-container">
<?php if (empty($orders)): ?>
<div class="empty-orders card">
<i class="fas fa-box-open" style="font-size: 3em; color: #ccc; margin-bottom: 20px;"></i>
<h3>No orders yet</h3>
<p>Orders for your products will appear here</p>
</div>
<?php else: ?>
<table class="orders-table">
<thead>
<tr>
<th>Order #</th>
<th>Customer</th>
<th>Items</th>
<th>Date</th>
<th>Total</th>
<th>Status</th>
<th>Pickup</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<?php foreach ($orders as $order): ?>
<tr>
<td>#<?php echo $order['order_id']; ?></td>
<td><?php echo htmlspecialchars($order['first_name'] . ' ' . $order['last_name']); ?></td>
<td>
<div class="order-items">
<?php
$items = explode(', ', $order['order_items']);
foreach ($items as $item) {
echo '<div class="order-item">';
// Check if it's a clothing item with size
if (strpos($item, '(Size:') !== false) {
// Split the item into name and size+quantity
$parts = explode(' (Size:', $item);
$name = $parts[0];
$sizeAndQty = $parts[1];
// Split size and quantity
$sizeQtyParts = explode(') x', $sizeAndQty);
$size = $sizeQtyParts[0];
$quantity = $sizeQtyParts[1];
echo '<span class="item-name">' . htmlspecialchars($name) . '</span>';
echo '<span class="item-size">Size: ' . htmlspecialchars($size) . '</span>';
echo '<span class="item-quantity">x' . htmlspecialchars($quantity) . '</span>';
} else {
echo htmlspecialchars($item);
}
echo '</div>';
}
?>
</div>
</td>
<td><?php echo date('M j, Y', strtotime($order['order_date'])); ?></td>
<td>₱<?php echo number_format($order['total_amount'], 2); ?></td>
<td>
<?php
$status_class = '';
switch ($order['status']) {
case 'pending':
$status_class = 'status-pending';
break;
case 'processing':
$status_class = 'status-processing';
break;
case 'ready_for_pickup':
$status_class = 'status-ready';
break;
case 'completed':
$status_class = 'status-completed';
break;
case 'cancelled':
$status_class = 'status-cancelled';
break;
}
?>
<span class="order-status <?php echo $status_class; ?>">
<?php echo ucfirst(str_replace('_', ' ', $order['status'])); ?>
</span>
</td>
<td>
<?php if ($order['pickup_schedule']): ?>
<?php echo date('M j, Y', strtotime($order['pickup_schedule'])); ?>
<?php else: ?>
Not scheduled
<?php endif; ?>
</td>
<td>
<?php if ($order['status'] !== 'completed' && $order['status'] !== 'cancelled'): ?>
<a href="/unimarket/owner/orders/schedule_pickup.php?order_id=<?php echo $order['order_id']; ?>" class="action-link">
<i class="fas fa-calendar-alt"></i> Schedule
</a>
<?php endif; ?>
<a href="/unimarket/owner/orders/view.php?order_id=<?php echo $order['order_id']; ?>" class="action-link">
<i class="fas fa-eye"></i> View
</a>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<?php endif; ?>
</div>
</div>
</div>
</div>
</div>
<?php include '../../includes/footer.php'; ?>
</body>
</html>
+253
View File
@@ -0,0 +1,253 @@
<?php
require_once '../../includes/auth_functions.php';
redirectIfNotLoggedIn();
redirectIfNotOwner();
require_once '../../config/database.php';
// Check if order ID is provided
if (!isset($_GET['order_id'])) {
header("Location: /unimarket/owner/orders/list.php");
exit();
}
$order_id = (int)$_GET['order_id'];
// Verify order contains owner's products
$stmt = $pdo->prepare("
SELECT o.order_id
FROM orders o
JOIN order_items oi ON o.order_id = oi.order_id
JOIN products p ON oi.product_id = p.product_id
WHERE o.order_id = ? AND p.owner_id = ?
LIMIT 1
");
$stmt->execute([$order_id, $_SESSION['user_id']]);
$valid_order = $stmt->fetch();
if (!$valid_order) {
header("Location: /unimarket/owner/orders/list.php");
exit();
}
// Get order details
$stmt = $pdo->prepare("
SELECT o.*, u.username, u.first_name, u.last_name
FROM orders o
JOIN users u ON o.customer_id = u.user_id
WHERE o.order_id = ?
");
$stmt->execute([$order_id]);
$order = $stmt->fetch();
// Handle form submission
$success_message = '';
$error_message = '';
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$pickup_date = $_POST['pickup_date'];
$pickup_time = $_POST['pickup_time'];
if (empty($pickup_date) || empty($pickup_time)) {
$error_message = 'Please select both date and time.';
} else {
$pickup_datetime = date('Y-m-d H:i:s', strtotime("$pickup_date $pickup_time"));
try {
$stmt = $pdo->prepare("UPDATE orders SET pickup_schedule = ?, status = 'ready' WHERE order_id = ?");
$stmt->execute([$pickup_datetime, $order_id]);
$success_message = 'Pickup scheduled successfully!';
$order['pickup_schedule'] = $pickup_datetime;
$order['status'] = 'ready';
} catch (PDOException $e) {
$error_message = 'Failed to schedule pickup. Please try again.';
}
}
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Schedule Pickup - UniMarket</title>
<link rel="stylesheet" href="/unimarket/assets/css/style.css">
<link rel="stylesheet" href="/unimarket/assets/css/responsive.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/flatpickr/dist/flatpickr.min.css">
<style>
.schedule-container {
margin-top: 30px;
}
.order-card {
background-color: white;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
padding: 20px;
margin-bottom: 30px;
}
.order-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
}
.customer-info {
margin-bottom: 20px;
}
.form-group {
margin-bottom: 20px;
}
.form-group label {
display: block;
margin-bottom: 8px;
font-weight: 500;
}
.form-group input, .form-group select {
width: 100%;
padding: 10px;
border: 1px solid #ddd;
border-radius: 4px;
font-size: 1rem;
}
.datetime-fields {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 15px;
}
.btn-submit {
background-color: var(--primary-color);
color: white;
border: none;
padding: 12px 20px;
border-radius: 4px;
cursor: pointer;
font-size: 1rem;
transition: background-color 0.3s;
}
.btn-submit:hover {
background-color: var(--primary-dark);
}
.back-link {
display: inline-block;
margin-bottom: 20px;
color: var(--primary-color);
text-decoration: none;
}
.back-link:hover {
text-decoration: underline;
}
@media (max-width: 768px) {
.datetime-fields {
grid-template-columns: 1fr;
}
}
</style>
</head>
<body>
<?php include '../../includes/header.php'; ?>
<div class="container schedule-container">
<a href="/unimarket/owner/orders/list.php" class="back-link">
<i class="fas fa-arrow-left"></i> Back to Orders
</a>
<h1>Schedule Pickup</h1>
<?php if ($success_message): ?>
<div class="alert alert-success"><?php echo $success_message; ?></div>
<?php endif; ?>
<?php if ($error_message): ?>
<div class="alert alert-error"><?php echo $error_message; ?></div>
<?php endif; ?>
<div class="order-card card">
<div class="order-header">
<h2>Order #<?php echo $order['order_id']; ?></h2>
<div>
<strong>Order Date:</strong> <?php echo date('M j, Y', strtotime($order['order_date'])); ?>
</div>
</div>
<div class="customer-info">
<h3>Customer Information</h3>
<p><strong>Name:</strong> <?php echo htmlspecialchars($order['first_name'] . ' ' . $order['last_name']); ?></p>
<p><strong>Username:</strong> <?php echo htmlspecialchars($order['username']); ?></p>
</div>
<form method="POST">
<div class="form-group">
<label>Current Pickup Schedule</label>
<p>
<?php if ($order['pickup_schedule']): ?>
<?php echo date('F j, Y g:i A', strtotime($order['pickup_schedule'])); ?>
<?php else: ?>
Not scheduled yet
<?php endif; ?>
</p>
</div>
<div class="form-group">
<label for="pickup_date">Pickup Date*</label>
<input type="date" id="pickup_date" name="pickup_date" required
min="<?php echo date('Y-m-d'); ?>">
</div>
<div class="form-group">
<label for="pickup_time">Pickup Time*</label>
<select id="pickup_time" name="pickup_time" required>
<option value="">Select a time</option>
<?php
// Generate time slots from 9AM to 5PM in 30-minute increments
$start = strtotime('09:00');
$end = strtotime('17:00');
for ($time = $start; $time <= $end; $time += 1800) {
$time_value = date('H:i', $time);
$time_display = date('g:i A', $time);
echo "<option value=\"$time_value\">$time_display</option>";
}
?>
</select>
</div>
<button type="submit" class="btn-submit">
<i class="fas fa-calendar-check"></i> Schedule Pickup
</button>
</form>
</div>
</div>
<?php include '../../includes/footer.php'; ?>
<script src="https://cdn.jsdelivr.net/npm/flatpickr"></script>
<script>
// Initialize date picker
flatpickr("#pickup_date", {
minDate: "today",
dateFormat: "Y-m-d",
disable: [
function(date) {
// Disable weekends
return (date.getDay() === 0 || date.getDay() === 6);
}
]
});
</script>
</body>
</html>
+467
View File
@@ -0,0 +1,467 @@
<?php
require_once '../../includes/auth_functions.php';
redirectIfNotLoggedIn();
redirectIfNotOwner();
require_once '../../config/database.php';
// Check if order ID is provided
if (!isset($_GET['order_id'])) {
header("Location: /unimarket/owner/orders/list.php");
exit();
}
$order_id = (int)$_GET['order_id'];
// Verify order contains owner's products
$stmt = $pdo->prepare("
SELECT o.order_id
FROM orders o
JOIN order_items oi ON o.order_id = oi.order_id
JOIN products p ON oi.product_id = p.product_id
WHERE o.order_id = ? AND p.owner_id = ?
LIMIT 1
");
$stmt->execute([$order_id, $_SESSION['user_id']]);
$valid_order = $stmt->fetch();
if (!$valid_order) {
header("Location: /unimarket/owner/orders/list.php");
exit();
}
// Get order details with proper null checks
$stmt = $pdo->prepare("
SELECT o.*,
COALESCE(u.username, 'N/A') AS username,
COALESCE(u.first_name, '') AS first_name,
COALESCE(u.last_name, '') AS last_name,
COALESCE(u.email, 'Not provided') AS email,
COALESCE(u.phone, 'Not provided') AS phone
FROM orders o
LEFT JOIN users u ON o.customer_id = u.user_id
WHERE o.order_id = ?
");
$stmt->execute([$order_id]);
$order = $stmt->fetch();
// Get order items for this owner's products
$stmt = $pdo->prepare("
SELECT oi.*, p.name, p.price, p.image_path, p.category, oi.size
FROM order_items oi
JOIN products p ON oi.product_id = p.product_id
WHERE oi.order_id = ? AND p.owner_id = ?
");
$stmt->execute([$order_id, $_SESSION['user_id']]);
$order_items = $stmt->fetchAll();
// Calculate subtotal for owner's products
$subtotal = 0;
foreach ($order_items as $item) {
$subtotal += $item['price'] * $item['quantity'];
}
// Handle status update
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['update_status'])) {
$new_status = $_POST['status'];
try {
$stmt = $pdo->prepare("UPDATE orders SET status = ? WHERE order_id = ?");
$stmt->execute([$new_status, $order_id]);
// Refresh order data
$stmt = $pdo->prepare("
SELECT o.*,
COALESCE(u.username, 'N/A') AS username,
COALESCE(u.first_name, '') AS first_name,
COALESCE(u.last_name, '') AS last_name,
COALESCE(u.email, 'Not provided') AS email,
COALESCE(u.phone, 'Not provided') AS phone
FROM orders o
LEFT JOIN users u ON o.customer_id = u.user_id
WHERE o.order_id = ?
");
$stmt->execute([$order_id]);
$order = $stmt->fetch();
$success_message = 'Order status updated successfully!';
} catch (PDOException $e) {
$error_message = 'Failed to update order status. Please try again.';
}
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Order #<?php echo $order_id; ?> - UniMarket</title>
<link rel="stylesheet" href="/unimarket/assets/css/style.css">
<link rel="stylesheet" href="/unimarket/assets/css/responsive.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
<style>
.order-detail-container {
margin-top: 30px;
}
.order-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
flex-wrap: wrap;
}
.order-card {
background-color: white;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
padding: 20px;
margin-bottom: 30px;
}
.customer-info {
margin-bottom: 30px;
}
.info-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 20px;
margin-top: 15px;
}
.info-item {
margin-bottom: 10px;
}
.info-label {
font-weight: 600;
color: #666;
margin-bottom: 5px;
}
.info-value {
font-size: 1.1rem;
}
.order-status {
display: inline-block;
padding: 8px 15px;
border-radius: 4px;
font-weight: 500;
}
.status-pending {
background-color: #fff3cd;
color: #856404;
}
.status-processing {
background-color: #cce5ff;
color: #004085;
}
.status-ready {
background-color: #d4edda;
color: #155724;
}
.status-completed {
background-color: #d4edda;
color: #155724;
font-weight: 600;
}
.status-cancelled {
background-color: #f8d7da;
color: #721c24;
}
.items-table {
width: 100%;
border-collapse: collapse;
margin-top: 20px;
}
.items-table th, .items-table td {
padding: 12px 15px;
text-align: left;
border-bottom: 1px solid #eee;
}
.items-table th {
background-color: #f5f5f5;
font-weight: 600;
}
.product-cell {
display: flex;
align-items: center;
}
.product-image {
width: 60px;
height: 60px;
margin-right: 15px;
background-color: #f9f9f9;
display: flex;
align-items: center;
justify-content: center;
overflow: hidden;
}
.product-image img {
max-width: 100%;
max-height: 100%;
}
.item-size {
color: #666;
font-size: 0.9em;
margin-top: 5px;
}
.order-total {
text-align: right;
margin-top: 20px;
font-size: 1.2rem;
font-weight: 600;
}
.back-link {
display: inline-block;
margin-bottom: 20px;
color: var(--primary-color);
text-decoration: none;
}
.back-link:hover {
text-decoration: underline;
}
.status-form {
display: flex;
gap: 10px;
align-items: center;
margin-top: 20px;
}
.status-form select {
padding: 8px;
border-radius: 4px;
border: 1px solid #ddd;
}
.status-form select:disabled,
.status-form button:disabled {
opacity: 0.6;
cursor: not-allowed;
}
.status-note {
display: inline-block;
margin-left: 10px;
color: #666;
font-style: italic;
}
.completed-note {
display: flex;
align-items: center;
gap: 8px;
color: #155724;
font-weight: 500;
padding: 8px 15px;
background-color: #d4edda;
border-radius: 4px;
}
.completed-note i {
font-size: 1.1rem;
}
@media (max-width: 768px) {
.items-table {
display: block;
overflow-x: auto;
}
.product-cell {
flex-direction: column;
align-items: flex-start;
}
.product-image {
margin-bottom: 10px;
}
.status-form {
flex-direction: column;
align-items: flex-start;
}
}
</style>
</head>
<body>
<?php include '../../includes/header.php'; ?>
<div class="container order-detail-container">
<a href="/unimarket/owner/orders/list.php" class="back-link">
<i class="fas fa-arrow-left"></i> Back to Orders
</a>
<div class="order-header">
<h1>Order #<?php echo $order_id; ?></h1>
<?php
$status_class = '';
switch ($order['status'] ?? '') {
case 'pending':
$status_class = 'status-pending';
break;
case 'processing':
$status_class = 'status-processing';
break;
case 'ready':
$status_class = 'status-ready';
break;
case 'completed':
$status_class = 'status-completed';
break;
case 'cancelled':
$status_class = 'status-cancelled';
break;
default:
$status_class = 'status-pending';
}
?>
<span class="order-status <?php echo $status_class; ?>">
<?php echo ucfirst(str_replace('_', ' ', $order['status'] ?? 'pending')); ?>
</span>
</div>
<?php if (isset($success_message)): ?>
<div class="alert alert-success"><?php echo $success_message; ?></div>
<?php endif; ?>
<?php if (isset($error_message)): ?>
<div class="alert alert-error"><?php echo $error_message; ?></div>
<?php endif; ?>
<div class="order-card card">
<h2>Customer Information</h2>
<div class="customer-info">
<div class="info-grid">
<div class="info-item">
<div class="info-label">Name</div>
<div class="info-value">
<?php
$first_name = $order['first_name'] ?? '';
$last_name = $order['last_name'] ?? '';
echo htmlspecialchars(trim("$first_name $last_name") ?: 'Not provided');
?>
</div>
</div>
<div class="info-item">
<div class="info-label">Username</div>
<div class="info-value"><?php echo htmlspecialchars($order['username'] ?? 'N/A'); ?></div>
</div>
<div class="info-item">
<div class="info-label">Email</div>
<div class="info-value"><?php echo htmlspecialchars($order['email'] ?? 'Not provided'); ?></div>
</div>
<div class="info-item">
<div class="info-label">Phone</div>
<div class="info-value"><?php echo htmlspecialchars($order['phone'] ?? 'Not provided'); ?></div>
</div>
</div>
</div>
<h2>Order Information</h2>
<div class="order-info">
<p><strong>Order Date:</strong> <?php echo date('F j, Y g:i A', strtotime($order['order_date'])); ?></p>
<p><strong>Total Amount:</strong> ₱<?php echo number_format($order['total_amount'], 2); ?></p>
<?php if ($order['status'] !== 'ready'): ?>
<p><strong>Pickup Schedule:</strong> <?php echo date('F j, Y g:i A', strtotime($order['pickup_schedule'])); ?></p>
<?php endif; ?>
</div>
<form method="POST" class="status-form">
<?php if ($order['status'] !== 'completed'): ?>
<label for="status">Update Status:</label>
<select name="status" id="status">
<option value="pending" <?php echo $order['status'] === 'pending' ? 'selected' : ''; ?>>Pending</option>
<option value="processing" <?php echo $order['status'] === 'processing' ? 'selected' : ''; ?>>Processing</option>
<option value="ready" <?php echo $order['status'] === 'ready' ? 'selected' : ''; ?>>Ready for Pickup</option>
<option value="completed" <?php echo $order['status'] === 'completed' ? 'selected' : ''; ?>>Completed</option>
<option value="cancelled" <?php echo $order['status'] === 'cancelled' ? 'selected' : ''; ?>>Cancelled</option>
</select>
<button type="submit" name="update_status" class="btn">
<i class="fas fa-save"></i> Update
</button>
<?php else: ?>
<div class="completed-note">
<i class="fas fa-check-circle"></i> Order is completed and cannot be modified
</div>
<?php endif; ?>
</form>
</div>
<div class="order-card card">
<h2>Order Items (Your Products)</h2>
<table class="items-table">
<thead>
<tr>
<th>Product</th>
<th>Price</th>
<th>Size</th>
<th>Quantity</th>
<th>Subtotal</th>
</tr>
</thead>
<tbody>
<?php foreach ($order_items as $item): ?>
<tr>
<td>
<div class="product-cell">
<div class="product-image">
<?php if ($item['image_path']): ?>
<img src="/unimarket/assets/images/products/<?php echo $item['image_path']; ?>" alt="<?php echo htmlspecialchars($item['name']); ?>">
<?php else: ?>
<i class="fas fa-box-open" style="color: #ccc;"></i>
<?php endif; ?>
</div>
<div>
<h4><?php echo htmlspecialchars($item['name']); ?></h4>
</div>
</div>
</td>
<td>₱<?php echo number_format($item['price'], 2); ?></td>
<td>
<?php if ($item['category'] === 'clothes' && $item['size']): ?>
<span class="item-size"><?php echo htmlspecialchars($item['size']); ?></span>
<?php else: ?>
-
<?php endif; ?>
</td>
<td><?php echo $item['quantity']; ?></td>
<td>₱<?php echo number_format($item['price'] * $item['quantity'], 2); ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<div class="order-total">
<div style="margin-bottom: 10px;">Subtotal: $<?php echo number_format($subtotal, 2); ?></div>
<div style="font-size: 1.4rem;">Total: $<?php echo number_format($subtotal, 2); ?></div>
</div>
</div>
</div>
<?php include '../../includes/footer.php'; ?>
</body>
</html>
+545
View File
@@ -0,0 +1,545 @@
<?php
require_once '../../includes/auth_functions.php';
redirectIfNotLoggedIn();
redirectIfNotOwner();
require_once '../../config/database.php';
$error_message = '';
$success_message = '';
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$name = trim($_POST['name']);
$description = trim($_POST['description']);
$price = trim($_POST['price']);
$category = trim($_POST['category']);
$subcategory = trim($_POST['subcategory']);
$stock_quantity = (int)$_POST['stock_quantity'];
// Get size data if category is clothes
$size_data = null;
if ($category === 'clothes' && isset($_POST['sizes']) && isset($_POST['size_quantities'])) {
$sizes = $_POST['sizes'];
$quantities = $_POST['size_quantities'];
$size_data = [];
for ($i = 0; $i < count($sizes); $i++) {
if (!empty($sizes[$i]) && isset($quantities[$i]) && $quantities[$i] > 0) {
$size_data[$sizes[$i]] = (int)$quantities[$i];
}
}
// Update stock quantity based on sizes
$stock_quantity = array_sum($size_data);
}
// Validate inputs
if (empty($name) || empty($description) || empty($price) || empty($stock_quantity)) {
$error_message = 'Please fill in all required fields.';
} elseif (!is_numeric($price) || $price <= 0) {
$error_message = 'Please enter a valid price.';
} elseif ($stock_quantity < 0) {
$error_message = 'Stock quantity cannot be negative.';
} elseif ($category === 'clothes' && empty($size_data)) {
$error_message = 'Please add at least one size with quantity.';
} else {
try {
// Handle file upload
$image_path = null;
if (isset($_FILES['image']) && $_FILES['image']['error'] === UPLOAD_ERR_OK) {
$upload_dir = '../../uploads/products/';
// Create directory if it doesn't exist
if (!file_exists($upload_dir)) {
mkdir($upload_dir, 0777, true);
}
$file_extension = pathinfo($_FILES['image']['name'], PATHINFO_EXTENSION);
$filename = uniqid() . '.' . $file_extension;
$destination = $upload_dir . $filename;
// Validate image
$allowed_types = ['jpg', 'jpeg', 'png', 'gif'];
if (!in_array(strtolower($file_extension), $allowed_types)) {
$error_message = 'Only JPG, JPEG, PNG, and GIF files are allowed.';
} elseif ($_FILES['image']['size'] > 5000000) { // 5MB max
$error_message = 'File size must be less than 5MB.';
} elseif (move_uploaded_file($_FILES['image']['tmp_name'], $destination)) {
$image_path = $filename;
} else {
$error_message = 'Failed to upload image.';
}
}
if (empty($error_message)) {
$pdo->beginTransaction();
try {
$stmt = $pdo->prepare("
INSERT INTO products
(owner_id, name, description, price, category, Sub_Cat, stock_quantity, image_path, size_data)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
");
$stmt->execute([
$_SESSION['user_id'],
$name,
$description,
$price,
$category,
$subcategory,
$stock_quantity,
$image_path,
$size_data ? json_encode($size_data) : null
]);
$pdo->commit();
$success_message = 'Product added successfully!';
// Clear form
$name = $description = $price = $category = $subcategory = '';
$stock_quantity = 0;
} catch (PDOException $e) {
$pdo->rollBack();
$error_message = 'Failed to add product: ' . $e->getMessage();
}
}
} catch (PDOException $e) {
$pdo->rollBack();
$error_message = 'Failed to add product. Please try again.';
}
}
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Add Product - UniMarket</title>
<link rel="stylesheet" href="/unimarket/assets/css/style.css">
<link rel="stylesheet" href="/unimarket/assets/css/responsive.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
<style>
.add-product-container {
margin-top: 30px;
}
.image-upload {
margin-bottom: 20px;
}
.image-preview {
width: 200px;
height: 200px;
background-color: #f5f5f5;
border: 1px dashed #ccc;
display: flex;
align-items: center;
justify-content: center;
margin-top: 10px;
overflow: hidden;
}
.image-preview.has-image {
border: none;
}
.image-preview img {
max-width: 100%;
max-height: 100%;
}
.default-text {
color: #999;
text-align: center;
padding: 20px;
}
.form-group {
margin-bottom: 20px;
}
.form-group label {
display: block;
margin-bottom: 8px;
font-weight: 500;
}
.form-group input,
.form-group textarea,
.form-group select {
width: 100%;
padding: 10px;
border: 1px solid #ddd;
border-radius: 4px;
font-size: 1rem;
}
.form-group textarea {
min-height: 100px;
}
.btn-submit {
background-color: var(--primary-color);
color: white;
border: none;
padding: 12px 20px;
border-radius: 4px;
cursor: pointer;
font-size: 1rem;
transition: background-color 0.3s;
}
.btn-submit:hover {
background-color: var(--primary-dark);
}
.subcategory-container {
display: flex;
align-items: center;
gap: 10px;
}
.subcategory-divider {
color: #666;
font-size: 0.9rem;
}
#subcategory, #customSubcategory {
flex: 1;
padding: 10px;
border: 1px solid #ddd;
border-radius: 4px;
font-size: 1rem;
}
#customSubcategory {
display: none;
}
.size-row {
display: flex;
gap: 10px;
margin-bottom: 10px;
align-items: center;
}
.size-select, .size-quantity {
padding: 8px;
border: 1px solid #ddd;
border-radius: 4px;
}
.size-select {
width: 100px;
}
.size-quantity {
width: 100px;
}
.btn-remove-size {
background: #ff4444;
color: white;
border: none;
border-radius: 4px;
width: 30px;
height: 30px;
cursor: pointer;
font-size: 18px;
display: flex;
align-items: center;
justify-content: center;
}
.btn-add-size {
background: var(--primary-color);
color: white;
border: none;
padding: 8px 16px;
border-radius: 4px;
cursor: pointer;
margin-top: 10px;
}
.btn-add-size:hover {
background: var(--primary-dark);
}
</style>
</head>
<body>
<?php include '../../includes/header.php'; ?>
<div class="container add-product-container">
<h1>Add New Product</h1>
<?php if ($success_message): ?>
<div class="alert alert-success"><?php echo $success_message; ?></div>
<?php endif; ?>
<?php if ($error_message): ?>
<div class="alert alert-error"><?php echo $error_message; ?></div>
<?php endif; ?>
<form method="POST" enctype="multipart/form-data" class="card">
<div class="form-group image-upload">
<label>Product Image</label>
<input type="file" name="image" id="productImage" accept="image/*">
<div class="image-preview" id="imagePreview">
<div class="default-text">No image selected</div>
</div>
</div>
<div class="form-group">
<label for="name">Product Name*</label>
<input type="text" id="name" name="name" value="<?php echo htmlspecialchars($name ?? ''); ?>" required>
</div>
<div class="form-group">
<label for="description">Description*</label>
<textarea id="description" name="description" required><?php echo htmlspecialchars($description ?? ''); ?></textarea>
</div>
<div class="form-group">
<label for="price">Price*</label>
<input type="number" id="price" name="price" step="0.01" min="0.01" value="<?php echo htmlspecialchars($price ?? ''); ?>" required>
</div>
<div class="form-group">
<label for="category">Category*</label>
<select id="category" name="category" required onchange="updateSubcategories()">
<option value="">Select a category</option>
<option value="clothes" <?php echo (isset($category) && $category === 'clothes') ? 'selected' : ''; ?>>Clothes</option>
<option value="school_supplies" <?php echo (isset($category) && $category === 'school_supplies') ? 'selected' : ''; ?>>School Supplies</option>
<option value="others" <?php echo (isset($category) && $category === 'others') ? 'selected' : ''; ?>>Others</option>
</select>
</div>
<div class="form-group" id="subcategoryGroup" style="display: none;">
<label for="subcategory">Subcategory*</label>
<div class="subcategory-container">
<select id="subcategory" name="subcategory" onchange="handleSubcategoryChange()">
<option value="">Select a subcategory</option>
</select>
<div class="subcategory-divider">or</div>
<input type="text" id="customSubcategory" name="customSubcategory" placeholder="Type your own subcategory">
</div>
</div>
<div class="form-group">
<label for="stock_quantity">Stock Quantity*</label>
<input type="number" id="stock_quantity" name="stock_quantity" min="0" value="<?php echo htmlspecialchars($stock_quantity ?? 0); ?>" required>
</div>
<div class="form-group" id="sizeGroup" style="display: none;">
<label>Sizes and Quantities*</label>
<div id="sizeContainer">
<!-- Size rows will be added here dynamically -->
</div>
<button type="button" class="btn-add-size" onclick="addSizeRow()">
<i class="fas fa-plus"></i> Add Size
</button>
</div>
<button type="submit" class="btn-submit">Add Product</button>
</form>
</div>
<?php include '../../includes/footer.php'; ?>
<script>
// Image preview functionality
const productImage = document.getElementById('productImage');
const imagePreview = document.getElementById('imagePreview');
const defaultText = imagePreview.querySelector('.default-text');
productImage.addEventListener('change', function() {
const file = this.files[0];
if (file) {
const reader = new FileReader();
reader.onload = function(e) {
imagePreview.innerHTML = '';
const img = document.createElement('img');
img.src = e.target.result;
imagePreview.appendChild(img);
imagePreview.classList.add('has-image');
}
reader.readAsDataURL(file);
} else {
imagePreview.innerHTML = '<div class="default-text">No image selected</div>';
imagePreview.classList.remove('has-image');
}
});
const subcategories = {
clothes: [
'T-Shirts',
'Pants',
'PE Uniform',
'University Uniform',
],
school_supplies: [
'Notebooks',
'Pens',
'Pencils',
'Highlighters',
'Markers',
'Art Supplies',
'Paper',
'Folders',
'Binders',
'Staplers'
],
others: [
'University Lace',
'Hygiene Products',
'Seals',
'Calculator',
'ID Holders'
]
};
function updateSubcategories() {
const categorySelect = document.getElementById('category');
const subcategorySelect = document.getElementById('subcategory');
const subcategoryGroup = document.getElementById('subcategoryGroup');
const customSubcategory = document.getElementById('customSubcategory');
const sizeGroup = document.getElementById('sizeGroup');
const stockQuantityInput = document.getElementById('stock_quantity');
const selectedCategory = categorySelect.value;
// Clear current options
subcategorySelect.innerHTML = '<option value="">Select a subcategory</option>';
if (selectedCategory && subcategories[selectedCategory]) {
// Show subcategory group
subcategoryGroup.style.display = 'block';
subcategorySelect.style.display = 'block';
customSubcategory.style.display = 'none';
// Add new options
subcategories[selectedCategory].forEach(subcategory => {
const option = document.createElement('option');
option.value = subcategory.toLowerCase().replace(/\s+/g, '_');
option.textContent = subcategory;
subcategorySelect.appendChild(option);
});
// Add custom option
const customOption = document.createElement('option');
customOption.value = 'custom';
customOption.textContent = 'Other (Type your own)';
subcategorySelect.appendChild(customOption);
// Show/hide size group based on category
if (selectedCategory === 'clothes') {
sizeGroup.style.display = 'block';
stockQuantityInput.style.display = 'none';
stockQuantityInput.value = calculateTotalSizeQuantity();
} else {
sizeGroup.style.display = 'none';
stockQuantityInput.style.display = 'block';
}
} else {
// Hide subcategory group if no category selected
subcategoryGroup.style.display = 'none';
sizeGroup.style.display = 'none';
stockQuantityInput.style.display = 'block';
}
}
function handleSubcategoryChange() {
const subcategorySelect = document.getElementById('subcategory');
const customSubcategory = document.getElementById('customSubcategory');
if (subcategorySelect.value === 'custom') {
subcategorySelect.style.display = 'none';
customSubcategory.style.display = 'block';
customSubcategory.focus();
customSubcategory.required = true;
} else {
subcategorySelect.style.display = 'block';
customSubcategory.style.display = 'none';
customSubcategory.required = false;
}
}
function addSizeRow() {
const container = document.getElementById('sizeContainer');
const newRow = document.createElement('div');
newRow.className = 'size-row';
newRow.innerHTML = `
<select name="sizes[]" class="size-select" onchange="updateTotalQuantity()">
<option value="">Select Size</option>
<option value="XS">XS</option>
<option value="S">S</option>
<option value="M">M</option>
<option value="L">L</option>
<option value="XL">XL</option>
<option value="XXL">XXL</option>
</select>
<input type="number" name="size_quantities[]" min="0" placeholder="Quantity" class="size-quantity" onchange="updateTotalQuantity()">
<button type="button" class="btn-remove-size" onclick="removeSizeRow(this)">×</button>
`;
container.appendChild(newRow);
}
function removeSizeRow(button) {
button.parentElement.remove();
updateTotalQuantity();
}
function calculateTotalSizeQuantity() {
const quantities = document.getElementsByName('size_quantities[]');
let total = 0;
for (let input of quantities) {
total += parseInt(input.value) || 0;
}
return total;
}
function updateTotalQuantity() {
const stockQuantityInput = document.getElementById('stock_quantity');
stockQuantityInput.value = calculateTotalSizeQuantity();
}
// Form submission validation
document.querySelector('form').addEventListener('submit', function(e) {
const category = document.getElementById('category').value;
const subcategorySelect = document.getElementById('subcategory');
const customSubcategory = document.getElementById('customSubcategory');
if (subcategorySelect.style.display === 'none' && !customSubcategory.value.trim()) {
e.preventDefault();
alert('Please enter a subcategory');
return;
}
if (category === 'clothes') {
const sizeSelects = document.getElementsByName('sizes[]');
const sizeQuantities = document.getElementsByName('size_quantities[]');
let hasValidSize = false;
for (let i = 0; i < sizeSelects.length; i++) {
if (sizeSelects[i].value && sizeQuantities[i].value > 0) {
hasValidSize = true;
break;
}
}
if (!hasValidSize) {
e.preventDefault();
alert('Please add at least one size with quantity');
return;
}
}
});
// Initialize subcategories on page load
document.addEventListener('DOMContentLoaded', function() {
updateSubcategories();
});
</script>
</body>
</html>
+513
View File
@@ -0,0 +1,513 @@
<?php
require_once '../../includes/auth_functions.php';
redirectIfNotLoggedIn();
redirectIfNotOwner();
require_once '../../config/database.php';
// Check if product ID is provided
if (!isset($_GET['id'])) {
header("Location: /unimarket/owner/products/list.php");
exit();
}
$product_id = (int)$_GET['id'];
// Verify product belongs to current owner
$stmt = $pdo->prepare("SELECT * FROM products WHERE product_id = ? AND owner_id = ?");
$stmt->execute([$product_id, $_SESSION['user_id']]);
$product = $stmt->fetch();
if (!$product) {
header("Location: /unimarket/owner/products/list.php");
exit();
}
$error_message = '';
$success_message = '';
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$name = trim($_POST['name']);
$description = trim($_POST['description']);
$price = trim($_POST['price']);
$category = trim($_POST['category']);
$stock_quantity = (int)$_POST['stock_quantity'];
// Get size data if category is clothes
$size_data = null;
if ($category === 'clothes' && isset($_POST['sizes']) && isset($_POST['size_quantities'])) {
$sizes = $_POST['sizes'];
$quantities = $_POST['size_quantities'];
$size_data = [];
for ($i = 0; $i < count($sizes); $i++) {
if (!empty($sizes[$i]) && isset($quantities[$i]) && $quantities[$i] > 0) {
$size_data[$sizes[$i]] = (int)$quantities[$i];
}
}
// Update stock quantity based on sizes
$stock_quantity = array_sum($size_data);
}
// Validate inputs
if (empty($name) || empty($description) || empty($price) || empty($stock_quantity)) {
$error_message = 'Please fill in all required fields.';
} elseif (!is_numeric($price) || $price <= 0) {
$error_message = 'Please enter a valid price.';
} elseif ($stock_quantity < 0) {
$error_message = 'Stock quantity cannot be negative.';
} elseif ($category === 'clothes' && empty($size_data)) {
$error_message = 'Please add at least one size with quantity.';
} else {
try {
$pdo->beginTransaction();
// Handle file upload if new image is provided
$image_path = $product['image_path'];
if (isset($_FILES['image']) && $_FILES['image']['error'] === UPLOAD_ERR_OK) {
$upload_dir = '../../assets/images/products/';
$file_extension = pathinfo($_FILES['image']['name'], PATHINFO_EXTENSION);
$filename = uniqid() . '.' . $file_extension;
$destination = $upload_dir . $filename;
// Validate image
$allowed_types = ['jpg', 'jpeg', 'png', 'gif'];
if (!in_array(strtolower($file_extension), $allowed_types)) {
$error_message = 'Only JPG, JPEG, PNG, and GIF files are allowed.';
} elseif ($_FILES['image']['size'] > 5000000) { // 5MB max
$error_message = 'File size must be less than 5MB.';
} else {
// Delete old image if it exists
if ($image_path && file_exists($upload_dir . $image_path)) {
unlink($upload_dir . $image_path);
}
if (move_uploaded_file($_FILES['image']['tmp_name'], $destination)) {
$image_path = $filename;
} else {
$error_message = 'Failed to upload image.';
}
}
}
if (empty($error_message)) {
$stmt = $pdo->prepare("
UPDATE products
SET name = ?, description = ?, price = ?, category = ?, stock_quantity = ?, image_path = ?, size_data = ?
WHERE product_id = ? AND owner_id = ?
");
$stmt->execute([
$name,
$description,
$price,
$category,
$stock_quantity,
$image_path,
$size_data ? json_encode($size_data) : null,
$product_id,
$_SESSION['user_id']
]);
if ($stmt->rowCount() > 0) {
$pdo->commit();
$success_message = 'Product updated successfully!';
// Refresh product data
$stmt = $pdo->prepare("SELECT * FROM products WHERE product_id = ?");
$stmt->execute([$product_id]);
$product = $stmt->fetch();
} else {
throw new Exception('No changes were made to the product.');
}
}
} catch (Exception $e) {
$pdo->rollBack();
$error_message = 'Failed to update product: ' . $e->getMessage();
}
}
}
// Decode size data if exists
$size_data = null;
if (isset($product['size_data']) && $product['size_data']) {
$size_data = json_decode($product['size_data'], true);
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Edit Product - UniMarket</title>
<link rel="stylesheet" href="/unimarket/assets/css/style.css">
<link rel="stylesheet" href="/unimarket/assets/css/responsive.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
<style>
.edit-product-container {
margin-top: 30px;
}
.image-upload {
margin-bottom: 20px;
}
.image-preview {
width: 200px;
height: 200px;
background-color: #f5f5f5;
border: 1px dashed #ccc;
display: flex;
align-items: center;
justify-content: center;
margin-top: 10px;
overflow: hidden;
}
.image-preview.has-image {
border: none;
}
.image-preview img {
max-width: 100%;
max-height: 100%;
}
.default-text {
color: #999;
text-align: center;
padding: 20px;
}
.form-group {
margin-bottom: 20px;
}
.form-group label {
display: block;
margin-bottom: 8px;
font-weight: 500;
}
.form-group input,
.form-group textarea,
.form-group select {
width: 100%;
padding: 10px;
border: 1px solid #ddd;
border-radius: 4px;
font-size: 1rem;
}
.form-group textarea {
min-height: 100px;
}
.btn-submit {
background-color: var(--primary-color);
color: white;
border: none;
padding: 12px 20px;
border-radius: 4px;
cursor: pointer;
font-size: 1rem;
transition: background-color 0.3s;
}
.btn-submit:hover {
background-color: var(--primary-dark);
}
.btn-delete {
background-color: #dc3545;
color: white;
border: none;
padding: 12px 20px;
border-radius: 4px;
cursor: pointer;
font-size: 1rem;
transition: background-color 0.3s;
margin-left: 10px;
}
.btn-delete:hover {
background-color: #c82333;
}
.button-group {
display: flex;
justify-content: space-between;
}
.size-row {
display: flex;
gap: 10px;
margin-bottom: 10px;
align-items: center;
}
.size-select, .size-quantity {
padding: 8px;
border: 1px solid #ddd;
border-radius: 4px;
}
.size-select {
width: 100px;
}
.size-quantity {
width: 100px;
}
.btn-remove-size {
background: #ff4444;
color: white;
border: none;
border-radius: 4px;
width: 30px;
height: 30px;
cursor: pointer;
font-size: 18px;
display: flex;
align-items: center;
justify-content: center;
}
.btn-add-size {
background: var(--primary-color);
color: white;
border: none;
padding: 8px 16px;
border-radius: 4px;
cursor: pointer;
margin-top: 10px;
}
.btn-add-size:hover {
background: var(--primary-dark);
}
</style>
</head>
<body>
<?php include '../../includes/header.php'; ?>
<div class="container edit-product-container">
<h1>Edit Product</h1>
<?php if ($success_message): ?>
<div class="alert alert-success"><?php echo $success_message; ?></div>
<?php endif; ?>
<?php if ($error_message): ?>
<div class="alert alert-error"><?php echo $error_message; ?></div>
<?php endif; ?>
<form method="POST" enctype="multipart/form-data" class="card">
<div class="form-group image-upload">
<label>Product Image</label>
<input type="file" name="image" id="productImage" accept="image/*">
<div class="image-preview" id="imagePreview">
<?php if ($product['image_path']): ?>
<img src="/unimarket/assets/images/products/<?php echo $product['image_path']; ?>" alt="<?php echo htmlspecialchars($product['name']); ?>">
<?php else: ?>
<div class="default-text">No image selected</div>
<?php endif; ?>
</div>
</div>
<div class="form-group">
<label for="name">Product Name*</label>
<input type="text" id="name" name="name" value="<?php echo htmlspecialchars($product['name']); ?>" required>
</div>
<div class="form-group">
<label for="description">Description*</label>
<textarea id="description" name="description" required><?php echo htmlspecialchars($product['description']); ?></textarea>
</div>
<div class="form-group">
<label for="price">Price*</label>
<input type="number" id="price" name="price" step="0.01" min="0.01" value="<?php echo htmlspecialchars($product['price']); ?>" required>
</div>
<div class="form-group">
<label for="category">Category</label>
<select id="category" name="category" onchange="updateSubcategories()">
<option value="">Select a category</option>
<option value="clothes" <?php echo $product['category'] === 'clothes' ? 'selected' : ''; ?>>Clothes</option>
<option value="school_supplies" <?php echo $product['category'] === 'school_supplies' ? 'selected' : ''; ?>>School Supplies</option>
<option value="others" <?php echo $product['category'] === 'others' ? 'selected' : ''; ?>>Others</option>
</select>
</div>
<div class="form-group" id="sizeGroup" style="display: <?php echo isset($product['category']) && $product['category'] === 'clothes' ? 'block' : 'none'; ?>;">
<label>Sizes and Quantities*</label>
<div id="sizeContainer">
<?php if ($size_data && is_array($size_data)): ?>
<?php foreach ($size_data as $size => $quantity): ?>
<div class="size-row">
<select name="sizes[]" class="size-select">
<option value="">Select Size</option>
<option value="XS" <?php echo $size === 'XS' ? 'selected' : ''; ?>>XS</option>
<option value="S" <?php echo $size === 'S' ? 'selected' : ''; ?>>S</option>
<option value="M" <?php echo $size === 'M' ? 'selected' : ''; ?>>M</option>
<option value="L" <?php echo $size === 'L' ? 'selected' : ''; ?>>L</option>
<option value="XL" <?php echo $size === 'XL' ? 'selected' : ''; ?>>XL</option>
<option value="XXL" <?php echo $size === 'XXL' ? 'selected' : ''; ?>>XXL</option>
</select>
<input type="number" name="size_quantities[]" min="0" placeholder="Quantity" class="size-quantity" value="<?php echo $quantity; ?>">
<button type="button" class="btn-remove-size" onclick="removeSizeRow(this)">×</button>
</div>
<?php endforeach; ?>
<?php else: ?>
<div class="size-row">
<select name="sizes[]" class="size-select">
<option value="">Select Size</option>
<option value="XS">XS</option>
<option value="S">S</option>
<option value="M">M</option>
<option value="L">L</option>
<option value="XL">XL</option>
<option value="XXL">XXL</option>
</select>
<input type="number" name="size_quantities[]" min="0" placeholder="Quantity" class="size-quantity">
<button type="button" class="btn-remove-size" onclick="removeSizeRow(this)">×</button>
</div>
<?php endif; ?>
</div>
<button type="button" class="btn-add-size" onclick="addSizeRow()">Add Another Size</button>
</div>
<div class="form-group">
<label for="stock_quantity">Stock Quantity*</label>
<input type="number" id="stock_quantity" name="stock_quantity" min="0" value="<?php echo htmlspecialchars($product['stock_quantity']); ?>" required>
</div>
<div class="button-group">
<button type="submit" class="btn-submit">Update Product</button>
<a href="/unimarket/owner/products/delete.php?id=<?php echo $product_id; ?>" class="btn-delete" onclick="return confirm('Are you sure you want to delete this product?')">
<i class="fas fa-trash"></i> Delete
</a>
</div>
</form>
</div>
<?php include '../../includes/footer.php'; ?>
<script>
// Image preview functionality
const productImage = document.getElementById('productImage');
const imagePreview = document.getElementById('imagePreview');
productImage.addEventListener('change', function() {
const file = this.files[0];
if (file) {
const reader = new FileReader();
reader.onload = function(e) {
imagePreview.innerHTML = '';
const img = document.createElement('img');
img.src = e.target.result;
imagePreview.appendChild(img);
imagePreview.classList.add('has-image');
}
reader.readAsDataURL(file);
} else {
<?php if ($product['image_path']): ?>
imagePreview.innerHTML = '<img src="/unimarket/assets/images/products/<?php echo $product['image_path']; ?>" alt="<?php echo htmlspecialchars($product['name']); ?>">';
<?php else: ?>
imagePreview.innerHTML = '<div class="default-text">No image selected</div>';
imagePreview.classList.remove('has-image');
<?php endif; ?>
}
});
function updateSubcategories() {
const categorySelect = document.getElementById('category');
const sizeGroup = document.getElementById('sizeGroup');
const stockQuantityInput = document.getElementById('stock_quantity');
const selectedCategory = categorySelect.value;
if (selectedCategory === 'clothes') {
sizeGroup.style.display = 'block';
stockQuantityInput.style.display = 'none';
stockQuantityInput.value = calculateTotalSizeQuantity();
} else {
sizeGroup.style.display = 'none';
stockQuantityInput.style.display = 'block';
}
}
function addSizeRow() {
const container = document.getElementById('sizeContainer');
const newRow = document.createElement('div');
newRow.className = 'size-row';
newRow.innerHTML = `
<select name="sizes[]" class="size-select" onchange="updateTotalQuantity()">
<option value="">Select Size</option>
<option value="XS">XS</option>
<option value="S">S</option>
<option value="M">M</option>
<option value="L">L</option>
<option value="XL">XL</option>
<option value="XXL">XXL</option>
</select>
<input type="number" name="size_quantities[]" min="0" placeholder="Quantity" class="size-quantity" onchange="updateTotalQuantity()">
<button type="button" class="btn-remove-size" onclick="removeSizeRow(this)">×</button>
`;
container.appendChild(newRow);
}
function removeSizeRow(button) {
button.parentElement.remove();
updateTotalQuantity();
}
function calculateTotalSizeQuantity() {
const quantities = document.getElementsByName('size_quantities[]');
let total = 0;
for (let input of quantities) {
total += parseInt(input.value) || 0;
}
return total;
}
function updateTotalQuantity() {
const stockQuantityInput = document.getElementById('stock_quantity');
stockQuantityInput.value = calculateTotalSizeQuantity();
}
// Form submission validation
document.querySelector('form').addEventListener('submit', function(e) {
const category = document.getElementById('category').value;
if (category === 'clothes') {
const sizeSelects = document.getElementsByName('sizes[]');
const sizeQuantities = document.getElementsByName('size_quantities[]');
let hasValidSize = false;
for (let i = 0; i < sizeSelects.length; i++) {
if (sizeSelects[i].value && sizeQuantities[i].value > 0) {
hasValidSize = true;
break;
}
}
if (!hasValidSize) {
e.preventDefault();
alert('Please add at least one size with quantity');
return;
}
}
});
// Initialize on page load
document.addEventListener('DOMContentLoaded', function() {
updateSubcategories();
});
</script>
</body>
</html>
+213
View File
@@ -0,0 +1,213 @@
<?php
require_once '../../includes/auth_functions.php';
redirectIfNotLoggedIn();
redirectIfNotOwner();
require_once '../../config/database.php';
// Get all products for this owner
$stmt = $pdo->prepare("SELECT * FROM products WHERE owner_id = ? ORDER BY created_at DESC");
$stmt->execute([$_SESSION['user_id']]);
$products = $stmt->fetchAll();
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>My Products - UniMarket</title>
<link rel="stylesheet" href="/unimarket/assets/css/style.css">
<link rel="stylesheet" href="/unimarket/assets/css/responsive.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
<style>
.products-container {
margin-top: 30px;
}
.products-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
flex-wrap: wrap;
}
.products-table {
width: 100%;
border-collapse: collapse;
margin-top: 20px;
}
.products-table th, .products-table td {
padding: 12px 15px;
text-align: left;
border-bottom: 1px solid #eee;
}
.products-table th {
background-color: #f5f5f5;
font-weight: 600;
}
.product-cell {
display: flex;
align-items: center;
}
.product-image {
width: 60px;
height: 60px;
margin-right: 15px;
background-color: #f9f9f9;
display: flex;
align-items: center;
justify-content: center;
overflow: hidden;
}
.product-image img {
max-width: 100%;
max-height: 100%;
}
.stock-low {
color: #e53935;
}
.action-link {
text-decoration: none;
margin-right: 10px;
padding: 5px 10px;
border-radius: 4px;
transition: background-color 0.3s ease;
}
.action-link.edit {
background-color: #28a745;
color: white;
}
.action-link.edit:hover {
background-color: #218838;
text-decoration: none;
}
.action-link.delete {
background-color: #dc3545;
color: white;
}
.action-link.delete:hover {
background-color: #c82333;
text-decoration: none;
}
.empty-products {
text-align: center;
padding: 50px;
background-color: #f9f9f9;
border-radius: 8px;
}
@media (max-width: 768px) {
.products-table {
display: block;
overflow-x: auto;
}
.product-cell {
flex-direction: column;
align-items: flex-start;
}
.product-image {
margin-bottom: 10px;
}
}
</style>
</head>
<body>
<?php include '../../includes/header.php'; ?>
<div class="container-fluid">
<div class="row">
<div class="col-12">
<div class="d-flex justify-content-between align-items-center mb-4">
<h1 class="h3 mb-0">Manage Products</h1>
<a href="../dashboard.php" class="btn btn-outline-secondary">
<i class="fas fa-arrow-left me-2"></i>Back to Dashboard
</a>
</div>
<div class="card">
<div class="container products-container">
<div class="products-header">
<h1>My Products</h1>
<a href="/unimarket/owner/products/add.php" class="btn" style="background-color: #28a745; color: white;">
<i class="fas fa-plus"></i> Add Product
</a>
</div>
<?php if (empty($products)): ?>
<div class="empty-products card">
<i class="fas fa-box-open" style="font-size: 3em; color: #ccc; margin-bottom: 20px;"></i>
<h3>You don't have any products yet</h3>
<p>Add your first product to start selling</p>
<a href="/unimarket/owner/products/add.php" class="btn">Add Product</a>
</div>
<?php else: ?>
<table class="products-table">
<thead>
<tr>
<th>Product</th>
<th>Price</th>
<th>Stock</th>
<th>Category</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<?php foreach ($products as $product): ?>
<tr>
<td>
<div class="product-cell">
<div class="product-image">
<?php if ($product['image_path']): ?>
<img src="/unimarket/uploads/products/<?php echo $product['image_path']; ?>" alt="<?php echo htmlspecialchars($product['name']); ?>">
<?php else: ?>
<i class="fas fa-box-open" style="color: #ccc;"></i>
<?php endif; ?>
</div>
<div>
<h4><?php echo htmlspecialchars($product['name']); ?></h4>
<p><?php echo substr(htmlspecialchars($product['description']), 0, 50); ?>...</p>
</div>
</div>
</td>
<td>₱<?php echo number_format($product['price'], 2); ?></td>
<td class="<?php echo $product['stock_quantity'] < 5 ? 'stock-low' : ''; ?>">
<?php echo $product['stock_quantity']; ?>
</td>
<td><?php echo htmlspecialchars($product['category']); ?></td>
<td>
<a href="/unimarket/owner/products/edit.php?id=<?php echo $product['product_id']; ?>" class="action-link edit">
<i class="fas fa-edit"></i> Edit
</a>
<a href="/unimarket/owner/products/delete.php?id=<?php echo $product['product_id']; ?>" class="action-link delete" onclick="return confirm('Are you sure you want to delete this product?')">
<i class="fas fa-trash"></i> Delete
</a>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<?php endif; ?>
</div>
</div>
</div>
</div>
</div>
<?php include '../../includes/footer.php'; ?>
</body>
</html>
Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 MiB

+2
View File
@@ -0,0 +1,2 @@
crischarles - passwordword
crischarlesvendor - password123