316 lines
11 KiB
HTML
316 lines
11 KiB
HTML
<!DOCTYPE html>
|
|
<!-- Declares this file as an HTML5 document -->
|
|
<html lang="en">
|
|
<head>
|
|
<!-- Sets the character encoding so text displays correctly -->
|
|
<meta charset="UTF-8">
|
|
|
|
<!-- Makes the page responsive on phones and tablets -->
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
|
|
<!-- The title shown in the browser tab -->
|
|
<title>MongoDB Products Manager</title>
|
|
|
|
<style>
|
|
/* Makes width and height calculations include padding and border */
|
|
* { box-sizing: border-box; }
|
|
|
|
/* Styles the whole page background, spacing, and default font */
|
|
body {
|
|
font-family: Arial, sans-serif;
|
|
background: #f4f4f4;
|
|
margin: 0;
|
|
padding: 16px;
|
|
}
|
|
|
|
/* Centers the main content and limits how wide it can grow */
|
|
.container {
|
|
max-width: 1200px;
|
|
margin: 0 auto;
|
|
}
|
|
|
|
/* Centers the page headings */
|
|
h1, h2 {
|
|
text-align: center;
|
|
}
|
|
|
|
/* Reusable white card-style section box */
|
|
.box {
|
|
background: white;
|
|
border-radius: 10px;
|
|
padding: 18px;
|
|
margin-bottom: 20px;
|
|
box-shadow: 0 2px 8px rgba(0,0,0,0.08);
|
|
}
|
|
|
|
/* Space below the flash message area */
|
|
.messages {
|
|
margin-bottom: 16px;
|
|
}
|
|
|
|
/* Base styling for any flash message */
|
|
.message {
|
|
padding: 10px 12px;
|
|
border-radius: 8px;
|
|
margin-bottom: 10px;
|
|
}
|
|
|
|
/* Green styling for success messages */
|
|
.message.success {
|
|
background: #e8f7ec;
|
|
color: #1e6b35;
|
|
border: 1px solid #bfe5c8;
|
|
}
|
|
|
|
/* Red styling for error messages */
|
|
.message.error {
|
|
background: #ffe8e8;
|
|
color: #8a1f1f;
|
|
border: 1px solid #e6bcbc;
|
|
}
|
|
|
|
/* Two-column layout for the "Add Product" form */
|
|
.form-grid {
|
|
display: grid;
|
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
gap: 10px;
|
|
}
|
|
|
|
/* Common styling for text and number inputs */
|
|
input[type="text"],
|
|
input[type="number"] {
|
|
width: 100%;
|
|
padding: 8px 10px;
|
|
border: 1px solid #bbb;
|
|
border-radius: 6px;
|
|
}
|
|
|
|
/* Makes an element span the full width of the form grid */
|
|
.full-width {
|
|
grid-column: 1 / -1;
|
|
}
|
|
|
|
/* Aligns checkbox and label nicely on one line */
|
|
.checkbox-row {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 8px;
|
|
}
|
|
|
|
/* Shared button styling for real buttons and link-buttons */
|
|
button, .btn {
|
|
display: inline-block;
|
|
padding: 8px 12px;
|
|
border: none;
|
|
border-radius: 6px;
|
|
text-decoration: none;
|
|
cursor: pointer;
|
|
font-size: 14px;
|
|
}
|
|
|
|
/* Green create button */
|
|
.btn-create { background: #28a745; color: white; }
|
|
|
|
/* Blue edit button */
|
|
.btn-edit { background: #007bff; color: white; }
|
|
|
|
/* Red delete button */
|
|
.btn-delete { background: #dc3545; color: white; }
|
|
|
|
/* Allows horizontal scrolling if the table is too wide */
|
|
.table-wrap {
|
|
overflow-x: auto;
|
|
}
|
|
|
|
/* Makes the table fill the available width */
|
|
table {
|
|
width: 100%;
|
|
border-collapse: collapse;
|
|
min-width: 1000px;
|
|
}
|
|
|
|
/* Styles the table cells */
|
|
th, td {
|
|
padding: 10px;
|
|
border-bottom: 1px solid #ddd;
|
|
text-align: left;
|
|
vertical-align: middle;
|
|
}
|
|
|
|
/* Light background for table headers */
|
|
th {
|
|
background: #f0f0f0;
|
|
}
|
|
|
|
/* Places edit and delete actions next to each other */
|
|
.actions {
|
|
display: flex;
|
|
gap: 8px;
|
|
align-items: center;
|
|
}
|
|
|
|
/* Message shown when there are no products */
|
|
.empty {
|
|
text-align: center;
|
|
color: #666;
|
|
padding: 20px;
|
|
}
|
|
|
|
/* On smaller screens, change the form to one column */
|
|
@media (max-width: 800px) {
|
|
.form-grid {
|
|
grid-template-columns: 1fr;
|
|
}
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<!-- Main centered page wrapper -->
|
|
<div class="container">
|
|
<!-- Main title of the page -->
|
|
<h1>MongoDB Products Manager</h1>
|
|
|
|
<!-- First card: flash messages and add-product form -->
|
|
<div class="box">
|
|
|
|
<!-- Jinja block: gets flashed messages from Flask -->
|
|
{% with messages = get_flashed_messages(with_categories=true) %}
|
|
|
|
<!-- Only show the message area if there are messages -->
|
|
{% if messages %}
|
|
<div class="messages">
|
|
|
|
<!-- Loop through each flashed message -->
|
|
{% for category, message in messages %}
|
|
|
|
<!-- category becomes a CSS class like success or error -->
|
|
<div class="message {{ category }}">{{ message }}</div>
|
|
{% endfor %}
|
|
</div>
|
|
{% endif %}
|
|
{% endwith %}
|
|
|
|
<!-- Section heading -->
|
|
<h2>Add Product</h2>
|
|
|
|
<!-- Form for creating a new product -->
|
|
<form action="/create" method="POST" class="form-grid">
|
|
|
|
<!-- Required numeric product ID -->
|
|
<input type="number" name="product_id" placeholder="Product ID" required>
|
|
|
|
<!-- Required product name -->
|
|
<input type="text" name="name" placeholder="Name" required>
|
|
|
|
<!-- Optional category -->
|
|
<input type="text" name="category" placeholder="Category">
|
|
|
|
<!-- Optional price with decimal support -->
|
|
<input type="number" step="0.01" name="price" placeholder="Price">
|
|
|
|
<!-- Optional rating with decimal support -->
|
|
<input type="number" step="0.1" name="rating" placeholder="Rating">
|
|
|
|
<!-- Optional comma-separated tags -->
|
|
<input type="text" name="tags" placeholder="Tags (comma separated)">
|
|
|
|
<!-- Optional supplier name -->
|
|
<input type="text" name="supplier_name" placeholder="Supplier Name">
|
|
|
|
<!-- Optional supplier country -->
|
|
<input type="text" name="supplier_country" placeholder="Supplier Country">
|
|
|
|
<!-- Checkbox row spanning full width -->
|
|
<div class="checkbox-row full-width">
|
|
<!-- Checked by default -->
|
|
<input type="checkbox" name="in_stock" id="in_stock" checked>
|
|
<label for="in_stock">In Stock</label>
|
|
</div>
|
|
|
|
<!-- Submit button spanning full width -->
|
|
<div class="full-width">
|
|
<button type="submit" class="btn-create">Create Product</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
|
|
<!-- Second card: product table -->
|
|
<div class="box">
|
|
<h2>Products</h2>
|
|
|
|
<!-- Only show the table if there are products -->
|
|
{% if products %}
|
|
<div class="table-wrap">
|
|
<table>
|
|
<thead>
|
|
<tr>
|
|
<!-- Table headers -->
|
|
<th>product_id</th>
|
|
<th>name</th>
|
|
<th>category</th>
|
|
<th>price</th>
|
|
<th>in_stock</th>
|
|
<th>rating</th>
|
|
<th>tags</th>
|
|
<th>supplier_name</th>
|
|
<th>supplier_country</th>
|
|
<th>created_at</th>
|
|
<th>actions</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
|
|
<!-- Loop through each product passed in from Flask -->
|
|
{% for product in products %}
|
|
<tr>
|
|
<!-- Display each product field -->
|
|
<td>{{ product.product_id }}</td>
|
|
<td>{{ product.name }}</td>
|
|
|
|
<!-- Show empty string if category is missing -->
|
|
<td>{{ product.category or '' }}</td>
|
|
|
|
<!-- Show empty string if price is blank -->
|
|
<td>{{ product.price if product.price != '' else '' }}</td>
|
|
|
|
<td>{{ product.in_stock }}</td>
|
|
|
|
<!-- Show empty string if rating is blank -->
|
|
<td>{{ product.rating if product.rating != '' else '' }}</td>
|
|
|
|
<!-- tags_text is already prepared in Python as a comma-separated string -->
|
|
<td>{{ product.tags_text }}</td>
|
|
|
|
<!-- Show supplier data if present -->
|
|
<td>{{ product.supplier_name or '' }}</td>
|
|
<td>{{ product.supplier_country or '' }}</td>
|
|
|
|
<!-- Show formatted creation date -->
|
|
<td>{{ product.created_at }}</td>
|
|
|
|
<td>
|
|
<div class="actions">
|
|
<!-- Link to edit page for this specific product -->
|
|
<a href="/edit/{{ product._id }}" class="btn btn-edit">Edit</a>
|
|
|
|
<!-- Small form to delete this product -->
|
|
<form action="/delete/{{ product._id }}" method="POST" style="margin:0;">
|
|
<!-- JavaScript confirm prevents accidental deletion -->
|
|
<button type="submit" class="btn-delete" onclick="return confirm('Delete this product?')">Delete</button>
|
|
</form>
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
|
|
<!-- If there are no products, show a placeholder message -->
|
|
{% else %}
|
|
<div class="empty">No products found yet.</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
</body>
|
|
</html> |