From 65ed818875b285f4d47a536c320b732aaf75c7d7 Mon Sep 17 00:00:00 2001 From: Midgard Date: Fri, 14 Aug 2020 04:57:02 +0200 Subject: [PATCH 01/13] Start redesign of order page --- app/app.py | 1 + app/models/order.py | 55 ++- app/models/orderitem.py | 5 +- app/static/css/main.css | 76 ++-- app/static/css/themes/plain_lightmode.css | 12 +- app/templates/order.html | 488 ++++++++++++++++------ app/templates/order_items.html | 10 +- app/templates/utils.html | 2 +- app/utils.py | 6 +- app/views/order.py | 17 +- 10 files changed, 446 insertions(+), 226 deletions(-) diff --git a/app/app.py b/app/app.py index 1a3dd98..1070142 100755 --- a/app/app.py +++ b/app/app.py @@ -171,6 +171,7 @@ def add_template_filters(app: Flask) -> None: app.template_filter("euro")(euro_string) app.template_filter("price_range")(price_range_string) app.template_filter("any")(any) + app.template_filter("all")(all) app = Flask(__name__) diff --git a/app/models/order.py b/app/models/order.py index 6ad5b5f..705470f 100644 --- a/app/models/order.py +++ b/app/models/order.py @@ -44,35 +44,48 @@ class Order(db.Model): ), "location_id must be configured before updating from HLDS" self.location_name = self.location.name - def group_by_user(self) -> typing.Dict[str, typing.Any]: + def for_user(self, anon=None, user=None) -> typing.List: + return list( + filter( + (lambda i: i.user == user) + if user is not None + else (lambda i: i.user_name == anon), + self.items + ) + ) + + def group_by_user(self) -> typing.List[typing.Tuple[str, typing.List]]: "Group items of an Order by user" - group: typing.Dict[str, typing.Any] = dict() + group: typing.Dict[str, typing.List] = dict() + for item in self.items: - user = group.get(item.get_name(), dict()) - user["total"] = user.get("total", 0) + item.price - user["to_pay"] = user.get("to_pay", 0) + item.price if not item.paid else 0 - user["paid"] = user.get("paid", True) and item.paid - user["dishes"] = user.get("dishes", []) + [item.dish_name] - group[str(item.get_name())] = user + if item.for_name not in group: + group[item.for_name] = [] - return group + group[item.for_name].append(item) - def group_by_dish( - self, sort_comments=False - ) -> typing.Dict[str, typing.Dict[str, typing.Any]]: + for _user_name, order_items in group.items(): + order_items.sort(key=lambda order_item: order_item.comment or "") + + return list(sorted(group.items())) + + def group_by_dish(self) -> typing.List[typing.Tuple[str, typing.List]]: "Group items of an Order by dish" - group: typing.Dict[str, typing.Dict[str, typing.Any]] = dict() + group: typing.Dict[str, typing.List] = dict() + for item in self.items: - dish = group.get(item.dish_name, dict()) - dish["count"] = dish.get("count", 0) + 1 - dish["comments"] = dish.get("comments", []) + [item.comment] - group[item.dish_name] = dish + if item.dish_name not in group: + group[item.dish_name] = [] - if sort_comments: - for _dish_name, dish_props in group.items(): - dish_props["comments"].sort() + group[item.dish_name].append(item) - return group + for _dish_name, order_items in group.items(): + order_items.sort(key=lambda order_item: ( + (order_item.comment or " No comment") + + (order_item.for_name) + )) + + return list(sorted(group.items())) def is_closed(self) -> bool: return self.stoptime and datetime.now() > self.stoptime diff --git a/app/models/orderitem.py b/app/models/orderitem.py index a078086..253cd85 100644 --- a/app/models/orderitem.py +++ b/app/models/orderitem.py @@ -37,7 +37,8 @@ class OrderItem(db.Model): raise ValueError("No Location found with id: " + location_id) raise AttributeError() - def get_name(self) -> str: + @property + def for_name(self) -> str: "Get the name of the user which 'owns' the item" if self.user_id is not None and self.user_id > 0: return self.user.username @@ -46,7 +47,7 @@ class OrderItem(db.Model): def __repr__(self) -> str: return "Order %d: %s wants %s" % ( self.order_id or 0, - self.get_name(), + self.for_name, self.dish_name or "None", ) diff --git a/app/static/css/main.css b/app/static/css/main.css index d5dc8b6..2fd83df 100644 --- a/app/static/css/main.css +++ b/app/static/css/main.css @@ -1,16 +1,15 @@ -/* Custom CSS */ :root { - /*Darkmode colors*/ - --dGray0:#D0D0D8; - --dGray1:#8E8E93; - --dGray2:#636366; - --dGray3:#48484A; - --dGray4:#3A3A3C; - --dGray5:#2C2C2E; - --dGray6:#1C1C1E; - --dBlue:#0A84FF; - --FontFamily:"Roboto","Helvetica Neue",Helvetica,Arial,sans-serif; - --FontSize:13px; + /* Darkmode colors */ + --dGray0: #D0D0D8; + --dGray1: #8E8E93; + --dGray2: #636366; + --dGray3: #48484A; + --dGray4: #3A3A3C; + --dGray5: #2C2C2E; + --dGray6: #1C1C1E; + --dBlue: #0A84FF; + --FontFamily: "Roboto","Helvetica Neue",Helvetica,Arial,sans-serif; + --FontSize: 13px; } html { @@ -26,7 +25,7 @@ body { font-size: var(--FontSize); } -.background{ +.background { position: absolute; z-index: -1000; top: 0; @@ -47,12 +46,6 @@ body { padding-top: 10px; } -.darker { - background-color: #fafafa; - margin-top: 10px; - padding-bottom: 5px; -} - @media (min-width: 992px) { .align-bottom { margin-top: 2.5em; @@ -121,10 +114,6 @@ body { margin-top: 1.3em; } -.order_row { - background: var(--dGray4); -} - .time_data{ display: flex; justify-content: space-between; @@ -144,14 +133,6 @@ body { } } -/* Add clickable box */ -div.box:hover { - cursor: hand; - cursor: pointer; - opacity: .9; - box-shadow: 2px 4px 4px -1px #888888; -} - a.divLink { position: absolute; width: 100%; @@ -198,18 +179,25 @@ a { .navbar { background-color: var(--dGray6); } -.navbar-default .navbar-nav .active a{ - background-color: var(--dGray4); +.navbar-default .navbar-nav .active a { + background-color: var(--dGray5); color: var(--dGray1); } -.navbar-default .navbar-nav .active a:hover{ - background-color: var(--dGray3); +.navbar-default .navbar-nav .active a:hover, +.navbar-default .navbar-nav .active a:focus, +.navbar-default .navbar-nav > li > a:hover, +.navbar-default .navbar-nav > li > a:focus { + background-color: var(--dGray4); color: var(--dGray0); } -.navbar-default .navbar-nav li a,.navbar-default .navbar-brand{ +.navbar-default .navbar-nav li a, +.navbar-default .navbar-brand { color: var(--dGray1); } -.navbar-default .navbar-nav li a:hover,.navbar-default .navbar-brand:hover{ +.navbar-default .navbar-nav li a:hover, +.navbar-default .navbar-nav li a:focus, +.navbar-default .navbar-brand:hover, +.navbar-default .navbar-brand:focus { color: var(--dGray0); } hr{ @@ -220,9 +208,16 @@ h1, h2, h3, h4, h5, h6{ color: var(--dGray1); } -.jumbotron, .darker { - background-color: var(--dGray4); +.jumbotron, .darker, .order_row { + background: var(--dGray4); } + +.darker { + margin-top: 10px; + padding-top: 5px; + padding-bottom: 5px; +} + .table tbody tr td { border-top: 1px solid var(--dGray3); } @@ -287,9 +282,6 @@ h1, h2, h3, h4, h5, h6{ .form-control::placeholder{ color: var(--dGray2); } -.enter_darkmode>a { - text-align: center; -} #dish_choices { margin: 0.5em 1em 1.5em; diff --git a/app/static/css/themes/plain_lightmode.css b/app/static/css/themes/plain_lightmode.css index 4713231..cb6baf7 100644 --- a/app/static/css/themes/plain_lightmode.css +++ b/app/static/css/themes/plain_lightmode.css @@ -1,10 +1,10 @@ :root { - --dGray0:#444444; - --dGray1:#666666; - --dGray2:#212121; - --dGray3:#ffffff; - --dGray4:#f9f9f9; - --dGray5:#ffffff; + --dGray0:#212121; + --dGray1:#444444; + --dGray2:#666666; + --dGray3:#cccccc; + --dGray4:#f1f1f1; + --dGray5:#f8f8f8; --dGray6:#ffffff; --dBlue:#0A84FF; } diff --git a/app/templates/order.html b/app/templates/order.html index 25a3f8a..2cbfc65 100644 --- a/app/templates/order.html +++ b/app/templates/order.html @@ -1,82 +1,123 @@ {% extends "layout.html" %} {% set active_page = "orders" -%} -{% set order_items = order.group_by_user() -%} +{% if current_user.is_anonymous() %} + {% set my_items = order.for_user(anon=session.get("anon_name", "")) %} +{% else %} + {% set my_items = order.for_user(user=current_user) %} +{% endif %} {% set courier_or_admin = not current_user.is_anonymous() and (current_user.is_admin() or current_user.id == order.courier_id) -%} {% import "utils.html" as util %} {% block container %} -
-
-

Order {{ order.id }} -
- {% if order.can_close(current_user.id) -%} -
- -
- {% endif %}{% if courier_or_admin %} - Edit - {%- endif %} -

- courier: {{ order.courier.username }} - {% if order.courier == None and not current_user.is_anonymous() %} -
- -
- {% endif %} -
- location: {% if order.location %} +
+

Order {{ order.id }}

+ +
+ {% if order.location %} {{ order.location_name }} {% else %} {{ order.location_name }} - {% endif %}
- {% if order.location.telephone != None %} - telephone: {{ order.location.telephone }}
{% endif %} - start: {{ order.starttime.strftime("%d/%m/%Y %H:%M") }}
- {% if order.stoptime %} - closing time: {{ order.stoptime.strftime("%H:%M") }} ({{ order.stoptime|countdown }}) - {% else %}open{% endif %}
- total price: {{ total_price|euro }} {% if courier_or_admin %}- remaining debts: {{ debts|euro }}{% endif %}
- {% if form -%} -
-

Order

+
+ +
+
+

Order information

+
+
Location
+
+ {% if order.location %} + {{ order.location_name }} + {% else %} + {{ order.location_name }} + {% endif %} +
+ +
Delivery at
+
+ Zeuskelder TODO +
+ +
Courier
+
+ {% if order.courier == None %} + {% if not current_user.is_anonymous() %} +
+ +
+ {% else %}No-one{% endif %} + {% else %} + {{ order.courier.username }} + {% endif %} +
+ +
+
+
Opens
+
{{ order.starttime.strftime("%d/%m/%Y %H:%M") }}
+ +
Closes
+
+ {% if order.stoptime %} + {{ order.stoptime.strftime("%H:%M") }} ({{ order.stoptime|countdown }}) + {% else %} + Never + {% endif %} +
+
+ +
+ {% if order.can_close(current_user.id) -%} +
+ +
+ {% endif %} + {% if courier_or_admin %} + Edit + {%- endif %} +
+
{% if form %}
+

Add item to order

+
- - Choose for me - {{ form.csrf_token }} + +
- {{ form.dish_id.label(class='control-label') }}
+ {{ form.dish_id.label }}
{{ form.dish_id(class='form-control select') }} {{ util.render_form_field_errors(form.dish_id) }}
-
{% if dish and dish.choices %} - {% for (choice_type, choice) in dish.choices %} -
-
- -
- {% endfor %} + {% for (choice_type, choice) in dish.choices %} +
+
+ +
+ {% endfor %} {% endif %}
- {{ form.comment.label(class='control-label') }}
+ {{ form.comment.label }}
{{ form.comment(class='form-control', placeholder='Fill in comment, when applicable') }} {{ util.render_form_field_errors(form.comment) }}
@@ -89,98 +130,165 @@
{% endif %}
+ Random + {{ form.submit_button(class='btn btn-primary') }} {% if not dish %}
If the chosen dish has options, they will be shown when you press submit, before adding the item to the order.
{% endif %}
-
- {%- endif %} -
-
-
-

Items

- - - {% if courier_or_admin %}{% endif %} - - - {% for item in order.items -%} - - - - - {% if courier_or_admin %} - - {% endif %} - + {%- endif %} + {{ item.price|euro }} {{ item.dish_name }}{% if item.comment %}; {{ item.comment }}{% endif %} + + {% endfor %} + + {% else %} +
(None)
+ {% endif %} + + + +
+
+

Ordered dishes

+ {% for dish_name, dish_order_items in order.group_by_dish() -%} + {% set has_comments = dish_order_items | map(attribute="comment") | any -%} +
+

+ {{ dish_order_items | length }} × + {{ dish_name }} +

+ + {% if has_comments -%} +
    + {% for item in dish_order_items -%} +
  • {% if item["comment"] %}{{ item["comment"] }} + {% else %}No comment + {% endif %} for {{ item.for_name }}
  • + {% endfor %} +
+ {% else %} + for {{ dish_order_items | map(attribute="for_name") | join(", ") }} + {%- endif %} + +

+
+ {%- endfor %} + +
+

Ordering at {{ order.location_name }}

+
+ {% if order.location.telephone %} +
Telephone
+
{{ order.location.telephone }}
+ {% endif %} + + {% if order.location.website %} +
Website
+
{{ order.location.website }}
+ {% endif %} + + {% if order.location.address or order.location.osm %} +
Location
+
+ {% if order.location.osm %} + {{ order.location.address or "View on OSM" }} + {% else %} + {{ order.location.address }} + {% endif %} +
+ {% endif %} +
+ TODO Only show options that allow you to order +
+
+ +
+
+

Items per person

+
NameItemPricePaid?Delete
{{ item.get_name() }}{{ item.dish_name }}{{ "*" if item.comment }}{{ item.price|euro }} - {% if not item.paid %} -
- -
- {% else %} - {% endif %} -
+
+

Add from history

+
    +
  • Todo
  • +
+
{% endif %}
+

My items

+ {% if my_items %} +
    + {% for item in my_items %} +
  • {% if item.can_delete(order.id, current_user.id, session.get('anon_name', '')) -%}
    - +
    - {%- endif %}
+ + + + + {% for user_name, order_items in order.group_by_user() -%} + + + + + {%- endfor %}
TotalNameItems
+ {% set paid = order_items | map(attribute="paid") | all %} + + + {{ order_items | map(attribute="price") | sum | euro }} + + {% if paid %}paid{% endif %} + {{ user_name }} +
    + {% for item in order_items %} +
  • +
    + {% if item.can_delete(order.id, current_user.id, session.get('anon_name', '')) -%} +
    + +
    + {% else %} + + {%- endif %} +
    + +
    {{ item.price|euro }}
    +
    {{ item.dish_name }}{{ "; " + item.comment if item.comment }}
    +
  • + {% endfor %} +
  • + +
  • +
+
+ +
-
-

Ordered dishes: {{ order.items.count() }}

- - {% for key, value in order.group_by_dish().items() -%} -
- {{ key }}: {{ value["count"] }} - {% if value["comments"]|any -%} -
    - {% for comment in value["comments"] -%} -
  • {% if comment %}{{ comment }} - {% else %}No comment - {% endif %}
  • - {% endfor %} -
- {%- endif %} -
- {%- endfor %} -
-
-
-
-

Debts

- - - {% if courier_or_admin %}{% endif %} - - - {% for key, value in order_items.items() -%} - - - - - {% if courier_or_admin %} - - {% endif %} - - {%- endfor %} - -
NameTotalTo payPaid?
{{ key }}{{ value["total"]|euro }}{{ value["to_pay"]|euro }} - {% if not value["to_pay"] == 0 %} -
- -
- {% else %} - - {% endif %} -
-
-
+ {% endblock %} {% block styles %} @@ -188,6 +296,122 @@ + + {% endblock %} {% block scripts %} {{ super() }} diff --git a/app/templates/order_items.html b/app/templates/order_items.html index 5dcb488..6d73795 100644 --- a/app/templates/order_items.html +++ b/app/templates/order_items.html @@ -33,13 +33,13 @@ Haldis - Order {{ order.id }} {% endif %} - {% for key, value in order.group_by_dish(True).items() -%} + {% for dish_name, dish_order_items in order.group_by_dish() -%}
-

{{ value["count"] }} × {{ key }}

- {% if value["comments"]|any -%} +

{{ dish_order_items | length }} × {{ dish_name }}

+ {% if dish_order_items | map(attribute="comment") | any -%}