Started with area page
This commit is contained in:
@@ -18,3 +18,13 @@ async def get_shop(request: Request, session: SessionDep):
|
||||
"shop.html.jinja",
|
||||
context={"request": request, "areas": areas},
|
||||
)
|
||||
|
||||
|
||||
@shop_router.get("/shop/area/{area_id}")
|
||||
async def get_shop(request: Request, session: SessionDep, area_id: int):
|
||||
query = select(Area).where(Area.id == area_id)
|
||||
area = session.scalars(query).one()
|
||||
return templates.TemplateResponse(
|
||||
"area.html.jinja",
|
||||
context={"request": request, "area": area},
|
||||
)
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import locale
|
||||
|
||||
from fastapi import Depends, FastAPI
|
||||
from fastapi.staticfiles import StaticFiles
|
||||
|
||||
@@ -5,6 +7,7 @@ from allmende_payment_system.api import root_router
|
||||
from allmende_payment_system.api.dependencies import get_user
|
||||
from allmende_payment_system.api.shop import shop_router
|
||||
|
||||
locale.setlocale(locale.LC_ALL, "de_DE.UTF-8")
|
||||
app = FastAPI(dependencies=[Depends(get_user)])
|
||||
|
||||
|
||||
|
||||
@@ -55,6 +55,15 @@ class Area(Base):
|
||||
description: Mapped[str] = mapped_column(nullable=True)
|
||||
image_path: Mapped[str] = mapped_column(nullable=True)
|
||||
|
||||
products: Mapped[list["Product"]] = relationship("Product")
|
||||
|
||||
|
||||
UnitsOfMeasure = typing.Literal[
|
||||
"g",
|
||||
"kg",
|
||||
"piece",
|
||||
]
|
||||
|
||||
|
||||
class Product(Base):
|
||||
__tablename__ = TABLE_PREFIX + "product"
|
||||
@@ -62,11 +71,14 @@ class Product(Base):
|
||||
name: Mapped[str] = mapped_column(nullable=False, unique=True)
|
||||
|
||||
price: Mapped[decimal.Decimal] = mapped_column(Numeric(10, 2))
|
||||
unit_of_measure: Mapped[UnitsOfMeasure] = mapped_column(nullable=False)
|
||||
# TODO: limit this to actually used vat rates?
|
||||
vat_rate: Mapped[decimal.Decimal] = mapped_column(Numeric(10, 2))
|
||||
|
||||
area_id: Mapped[int] = mapped_column(ForeignKey(TABLE_PREFIX + "area.id"))
|
||||
area: Mapped["Area"] = relationship("Area")
|
||||
area: Mapped["Area"] = relationship("Area", back_populates="products")
|
||||
|
||||
image_path: Mapped[str] = mapped_column(nullable=True)
|
||||
|
||||
|
||||
TransactionTypes = typing.Literal[
|
||||
|
||||
BIN
src/allmende_payment_system/static/img/placeholder.jpg
Normal file
BIN
src/allmende_payment_system/static/img/placeholder.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.8 KiB |
36
src/allmende_payment_system/templates/area.html.jinja
Normal file
36
src/allmende_payment_system/templates/area.html.jinja
Normal file
@@ -0,0 +1,36 @@
|
||||
{% extends "base.html.jinja" %}
|
||||
{% block content %}
|
||||
<!-- Area Header -->
|
||||
<div class="mb-4">
|
||||
<h2 class="h4 mb-3">{{ area.name }}</h2>
|
||||
<p class="text-muted">{{ area.description or ''}} </p>
|
||||
</div>
|
||||
|
||||
<!-- Products Grid -->
|
||||
<div class="row g-3">
|
||||
{% for product in area.products %}
|
||||
<div class="col-md-6 col-lg-4">
|
||||
<div class="card h-100 border-0 shadow-sm">
|
||||
<a href="#" class="text-decoration-none text-dark">
|
||||
<!-- Product Image -->
|
||||
<img
|
||||
src="/static/img/{{ product.image_path if product.image_path else 'placeholder.jpg' }}"
|
||||
alt="{{ product.name }}"
|
||||
class="card-img-top img-fluid rounded-top"
|
||||
style="height: 100px; object-fit: cover;"
|
||||
>
|
||||
<!-- Product Details -->
|
||||
<div class="card-body">
|
||||
<h5 class="card-title mb-2">{{ product.name }}</h5>
|
||||
<p class="card-text text-muted small mb-3">{{ product.description }}</p>
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<span class="fw-bold">{{ product.price|format_number }} € pro {{ product.unit_of_measure|units_of_measure_de }}</span>
|
||||
<button class="btn btn-sm btn-outline-primary">Add to Cart</button>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -9,13 +9,13 @@
|
||||
<div class="row g-3">
|
||||
{% for area in areas %}
|
||||
<div class="col-md-6 col-lg-4">
|
||||
<a href="#" class="text-decoration-none">
|
||||
<a href="/shop/area/{{ area.id }}" class="text-decoration-none">
|
||||
<div class="card h-100 border-0 shadow-sm">
|
||||
<div class="card-body d-flex align-items-center p-3">
|
||||
<!-- Image on the left -->
|
||||
<div class="me-3" style="width: 120px; flex-shrink: 0;">
|
||||
<img
|
||||
src="/static/img/{{ area.image_path }}"
|
||||
src="/static/img/{{ area.image_path if area.image_path !='' else 'placeholder.png'}}" }}"
|
||||
alt="{{ area.name }}"
|
||||
class="img-fluid rounded"
|
||||
style="max-height: 100px; width: 100%; object-fit: contain;"
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
import locale
|
||||
import numbers
|
||||
|
||||
from starlette.templating import Jinja2Templates
|
||||
|
||||
TRANSACTION_TYPE_DE = {
|
||||
@@ -7,8 +10,19 @@ TRANSACTION_TYPE_DE = {
|
||||
"product": "Einkauf",
|
||||
}
|
||||
|
||||
UNITS_OF_MEASURE = {"piece": "Stück"}
|
||||
|
||||
|
||||
def format_number(value: float):
|
||||
try:
|
||||
return f"{value:n}"
|
||||
except TypeError:
|
||||
return value
|
||||
|
||||
|
||||
def get_jinja_renderer() -> Jinja2Templates:
|
||||
renderer = Jinja2Templates(directory="src/allmende_payment_system/templates")
|
||||
renderer.env.filters["transaction_type_de"] = lambda x: TRANSACTION_TYPE_DE[x]
|
||||
renderer.env.filters["units_of_measure_de"] = lambda x: UNITS_OF_MEASURE.get(x, x)
|
||||
renderer.env.filters["format_number"] = format_number
|
||||
return renderer
|
||||
|
||||
Reference in New Issue
Block a user