This commit is contained in:
ApfelTeeSaft
2024-09-09 11:56:29 +02:00
parent 0f56ad1f34
commit a86c9e85fc
4 changed files with 434 additions and 0 deletions
+123
View File
@@ -0,0 +1,123 @@
from flask import Flask, request, jsonify, render_template, send_from_directory
from flask_sqlalchemy import SQLAlchemy
from datetime import datetime
import os
from werkzeug.utils import secure_filename
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///products.db'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
app.config['UPLOAD_FOLDER'] = 'static/uploads'
os.makedirs(app.config['UPLOAD_FOLDER'], exist_ok=True)
db = SQLAlchemy(app)
class Product(db.Model):
id = db.Column(db.Integer, primary_key=True)
product_name = db.Column(db.String(100), nullable=False)
product_type = db.Column(db.String(50), nullable=False)
date_bought = db.Column(db.Date, nullable=False)
price_bought = db.Column(db.Float, nullable=False)
date_sold = db.Column(db.Date, nullable=True)
price_sold = db.Column(db.Float, nullable=True)
condition = db.Column(db.String(20), nullable=False)
image = db.Column(db.String(255), nullable=True)
is_sold = db.Column(db.Boolean, default=False)
with app.app_context():
db.create_all()
@app.route('/')
def index():
return render_template('index.html')
@app.route('/products', methods=['POST'])
def add_product():
product_name = request.form['product_name']
product_type = request.form['product_type']
date_bought = datetime.strptime(request.form['date_bought'], '%d/%m/%Y').date()
price_bought = float(request.form['price_bought'])
condition = request.form['condition']
image_file = request.files['image']
if image_file:
filename = secure_filename(image_file.filename)
image_path = os.path.join(app.config['UPLOAD_FOLDER'], filename)
image_file.save(image_path)
image_url = f"/{image_path}"
else:
image_url = None
new_product = Product(
product_name=product_name,
product_type=product_type,
date_bought=date_bought,
price_bought=price_bought,
condition=condition,
image=image_url,
is_sold=False
)
db.session.add(new_product)
db.session.commit()
return jsonify({'message': 'Product added successfully!'}), 201
@app.route('/products/<int:product_id>', methods=['PUT'])
def update_product(product_id):
product = Product.query.get_or_404(product_id)
product.product_name = request.form['product_name']
product.product_type = request.form['product_type']
product.date_bought = datetime.strptime(request.form['date_bought'], '%d/%m/%Y').date()
product.price_bought = float(request.form['price_bought'])
product.condition = request.form['condition']
if 'image' in request.files:
image_file = request.files['image']
if image_file:
filename = secure_filename(image_file.filename)
image_path = os.path.join(app.config['UPLOAD_FOLDER'], filename)
image_file.save(image_path)
product.image = f"/{image_path}"
product.is_sold = 'is_sold' in request.form and request.form['is_sold'] == 'on'
if product.is_sold:
product.date_sold = datetime.strptime(request.form['date_sold'], '%d/%m/%Y').date()
product.price_sold = float(request.form['price_sold'])
else:
product.date_sold = None
product.price_sold = None
db.session.commit()
return jsonify({'message': 'Product updated successfully!'}), 200
@app.route('/products', methods=['GET'])
def get_products():
products = Product.query.all()
result = []
for product in products:
result.append({
'id': product.id,
'product_name': product.product_name,
'product_type': product.product_type,
'date_bought': product.date_bought.strftime('%d/%m/%Y'),
'price_bought': product.price_bought,
'date_sold': product.date_sold.strftime('%d/%m/%Y') if product.date_sold else None,
'price_sold': product.price_sold,
'condition': product.condition,
'image': product.image,
'is_sold': product.is_sold
})
return jsonify(result), 200
@app.route('/static/<path:path>')
def send_static(path):
return send_from_directory('static', path)
if __name__ == '__main__':
app.run(debug=True)
+117
View File
@@ -0,0 +1,117 @@
body {
font-family: Arial, sans-serif;
background-color: #f0f2f5;
margin: 0;
padding: 0;
}
.container {
width: 80%;
margin: 0 auto;
padding-top: 30px;
}
header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
}
h1 {
color: #333;
margin: 0;
}
.add-button {
background-color: #28a745;
color: white;
border: none;
padding: 10px 15px;
border-radius: 50%;
font-size: 24px;
cursor: pointer;
}
.add-button:hover {
background-color: #218838;
}
.product-list {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
gap: 20px;
}
.product-item {
background-color: white;
padding: 15px;
border-radius: 8px;
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
}
.product-item img {
width: 100%;
height: 150px;
object-fit: cover;
border-radius: 5px;
margin-bottom: 10px;
}
.pagination {
display: flex;
justify-content: center;
margin-top: 20px;
}
.pagination button {
margin: 0 5px;
padding: 5px 10px;
background-color: #007bff;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
}
.pagination button:hover {
background-color: #0056b3;
}
.modal {
display: none;
position: fixed;
z-index: 1;
left: 0;
top: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.5);
}
.modal-content {
background-color: #fff;
margin: 15% auto;
padding: 20px;
border-radius: 10px;
width: 40%;
}
.close-btn {
float: right;
font-size: 28px;
cursor: pointer;
}
.submit-button {
background-color: #007bff;
color: white;
border: none;
padding: 10px;
border-radius: 5px;
cursor: pointer;
}
.submit-button:hover {
background-color: #0056b3;
}
+132
View File
@@ -0,0 +1,132 @@
document.addEventListener('DOMContentLoaded', () => {
const productList = document.getElementById('product-list');
const pagination = document.getElementById('pagination');
const modal = document.getElementById('modal');
const closeBtn = document.querySelector('.close-btn');
const addProductBtn = document.getElementById('add-product-btn');
const productForm = document.getElementById('product-form');
const modalTitle = document.getElementById('modal-title');
const isSoldCheckbox = document.getElementById('is_sold');
const soldFields = document.getElementById('sold-fields');
let currentPage = 1;
const itemsPerPage = 10;
let editMode = false;
let editProductId = null;
function fetchProducts() {
fetch('/products')
.then(response => response.json())
.then(data => renderProducts(data))
.catch(error => console.error('Error fetching products:', error));
}
function renderProducts(products) {
productList.innerHTML = '';
pagination.innerHTML = '';
const totalPages = Math.ceil(products.length / itemsPerPage);
const start = (currentPage - 1) * itemsPerPage;
const end = start + itemsPerPage;
const currentItems = products.slice(start, end);
currentItems.forEach(product => {
const productItem = document.createElement('div');
productItem.classList.add('product-item');
productItem.innerHTML = `
<img src="${product.image}" alt="${product.product_name}">
<h3>${product.product_name}</h3>
<p>Type: ${product.product_type}</p>
<p>Date Bought: ${product.date_bought}</p>
<p>Price Bought: €${product.price_bought}</p>
<p>Condition: ${product.condition}</p>
<p>Sold: ${product.is_sold ? 'Yes' : 'No'}</p>
${product.is_sold ? `<p>Date Sold: ${product.date_sold}</p><p>Price Sold: $${product.price_sold}</p>` : ''}
<button class="edit-button" data-id="${product.id}">Edit</button>
`;
productList.appendChild(productItem);
});
for (let i = 1; i <= totalPages; i++) {
const button = document.createElement('button');
button.textContent = i;
if (i === currentPage) button.disabled = true;
button.addEventListener('click', () => {
currentPage = i;
renderProducts(products);
});
pagination.appendChild(button);
}
document.querySelectorAll('.edit-button').forEach(button => {
button.addEventListener('click', () => {
editMode = true;
editProductId = button.getAttribute('data-id');
openModalForEdit(editProductId);
});
});
}
addProductBtn.addEventListener('click', () => {
editMode = false;
editProductId = null;
modalTitle.textContent = 'Add New Product';
productForm.reset();
soldFields.style.display = 'none';
modal.style.display = 'block';
});
closeBtn.addEventListener('click', () => {
modal.style.display = 'none';
});
isSoldCheckbox.addEventListener('change', () => {
soldFields.style.display = isSoldCheckbox.checked ? 'block' : 'none';
});
productForm.addEventListener('submit', (event) => {
event.preventDefault();
const formData = new FormData(productForm);
const url = editMode ? `/products/${editProductId}` : '/products';
const method = editMode ? 'PUT' : 'POST';
fetch(url, {
method: method,
body: formData
})
.then(response => response.json())
.then(data => {
fetchProducts();
modal.style.display = 'none';
productForm.reset();
})
.catch(error => console.error('Error adding/updating product:', error));
});
function openModalForEdit(productId) {
fetch(`/products`)
.then(response => response.json())
.then(products => {
const product = products.find(p => p.id == productId);
if (product) {
modalTitle.textContent = 'Edit Product';
document.getElementById('product_name').value = product.product_name;
document.getElementById('product_type').value = product.product_type;
document.getElementById('date_bought').value = product.date_bought;
document.getElementById('price_bought').value = product.price_bought;
document.getElementById('condition').value = product.condition;
isSoldCheckbox.checked = product.is_sold;
if (product.is_sold) {
document.getElementById('date_sold').value = product.date_sold ? product.date_sold : '';
document.getElementById('price_sold').value = product.price_sold ? product.price_sold : '';
soldFields.style.display = 'block';
} else {
soldFields.style.display = 'none';
}
modal.style.display = 'block';
}
})
.catch(error => console.error('Error fetching product details:', error));
}
fetchProducts();
});
+62
View File
@@ -0,0 +1,62 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Product Inventory</title>
<link rel="stylesheet" href="{{ url_for('static', filename='css/styles.css') }}">
</head>
<body>
<div class="container">
<header>
<h1>Product Inventory</h1>
<button id="add-product-btn" class="add-button">+</button>
</header>
<div id="product-list" class="product-list"></div>
<div id="pagination" class="pagination"></div>
<div id="modal" class="modal">
<div class="modal-content">
<span class="close-btn">&times;</span>
<h2 id="modal-title">Add New Product</h2>
<form id="product-form" enctype="multipart/form-data">
<label for="product_name">Product Name:</label>
<input type="text" id="product_name" name="product_name" required>
<label for="product_type">Type:</label>
<input type="text" id="product_type" name="product_type" required>
<label for="date_bought">Date Bought:</label>
<input type="date" id="date_bought" name="date_bought" required>
<label for="price_bought">Price Bought:</label>
<input type="number" id="price_bought" name="price_bought" step="0.01" required>
<label for="condition">Condition:</label>
<input type="text" id="condition" name="condition" required>
<label for="image">Image:</label>
<input type="file" id="image" name="image" accept="image/*">
<label for="is_sold">Mark as Sold:</label>
<input type="checkbox" id="is_sold" name="is_sold">
<div id="sold-fields" style="display: none;">
<label for="date_sold">Date Sold:</label>
<input type="date" id="date_sold" name="date_sold">
<label for="price_sold">Price Sold:</label>
<input type="number" id="price_sold" name="price_sold" step="0.01">
</div>
<button type="submit" class="submit-button">Save Product</button>
</form>
</div>
</div>
</div>
<script src="{{ url_for('static', filename='js/script.js') }}"></script>
</body>
</html>