haldis/app/templates/order.html
Midgard d20ce9803e
Improve design
Remove main CSS from shop view page. Make it theme independent and
maximize contrast.

Improve spacing in "add item" list.

Refactor old term "showcase" to "shop_view" in code.
2021-06-21 00:46:29 +02:00

548 lines
14 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

{% extends "layout.html" %}
{% set active_page = "orders" -%}
{% 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 metas %}
{{ super() }}
<meta name="robots" content="noindex, nofollow">
{% endblock %}
{% block container %}
<header>
<h2 id="order-title">Order {{ order.id }}</h2>
<div class="location">
{% if order.location %}
<a href="{{ url_for('general_bp.location', location_id=order.location_id) }}">{{ order.location_name }}</a>
{% else %}
{{ order.location_name }}
{% endif %}
</div>
</header>
<section>
<div class="column">
<div class="box" id="my_items">
<h3>My items</h3>
{% if my_items %}
<ul>
{% for item in my_items %}
<li class="spacecake">
{% if item.can_delete(order.id, current_user.id, session.get('anon_name', '')) -%}
<form action="{{ url_for('order_bp.delete_item', order_id=order.id, item_id=item.id) }}" method="post" style="display:inline">
<button class="btn btn-link btn-sm" type="submit" style="padding: 0 0.5em;"><span class="glyphicon glyphicon-remove"></span></button>
</form>
{%- endif %}
<span>{{ item.dish_name }}{% if item.comment %}; {{ item.comment }}{% endif %}</span><span class="spacer"> </span><span class="price">{{ item.price | euro }}</span>
</li>
{% endfor %}
</ul>
{% else %}
<div>(None)</div>
{% endif %}
</div>
{% if form %}
<!--
<div class="box" id="from_favourites">
<h3>Add from favourites</h3>
<ul>
<li>Todo</li>
</ul>
</div>
-->
<div class="box" id="add_item">
<h3>Add item to order</h3>
{% for dish in order.location.dishes %}
<form method="post" action="{{ url_for('order_bp.order_item_create', order_id=order.id) }}" id="dish_{{ dish.id }}">
{{ form.csrf_token }}
<input type="hidden" name="dish_id" value="{{ dish.id }}" />
{% if form.dish_id.errors %}
<div class="form-group has-errors">
{{ util.render_form_field_errors(form.dish_id) }}
</div>
{% endif %}
<details {% if dish.id == selected_dish.id %}open="open"{% endif %}>
<summary class="spacecake">
<span class="dish_name">{{ dish.name }}</span>
<!--
{% if dish.tags %}<span class="tags"> {{ dish.tags | join(", ") }}</span>{% endif %}
-->
<span class="spacer"></span>
<span class="price">{{ dish.price_range() | price_range }}</span>
</summary>
{% if dish.description %}<div class="description">{{ dish.description }}</div>{% endif %}
{% for (choice_type, choice) in dish.choices %}
<div class="form-group select2-container select2">
<label class="control-label" for="choice_{{ choice.id }}">{{ choice.name }}</label><br/>
<select
{{ "multiple=multiple" if choice_type=="multi_choice" else "required=required" }}
name="choice_{{ choice.id }}"
class="form-control select">
{% for option in choice.options %}
<option value="{{ option.id }}"><!--
-->{{ option.name }}{{ ": " + option.price|euro if option.price else "" }}<!--
-->{{ " (" + option.description + ")" if option.description else "" }}<!--
--></option>
{% endfor %}
</select>
</div>
{% endfor %}
<div class="form-group {{ 'has-errors' if form.dish_id.errors }}">
{{ form.comment.label }}<br>
{{ form.comment(class='form-control', placeholder='Fill in comment, when applicable') }}
{{ util.render_form_field_errors(form.comment) }}
</div>
{% if current_user.is_anonymous() %}
<div class="form-group{{ ' has-error' if form.user_name.errors }}{{ ' required' if form.user_name.flags.required }}">
{{ form.user_name.label(class='control-label') }}
{{ form.user_name(class='form-control', placeholder='Fill in your name...') }}
{{ util.render_form_field_errors(form.user_name) }}
</div>
{% endif %}
<div class="form-group" style="padding-top: 8px;">
{{ form.submit_button(class='btn btn-primary') }}
</div>
</details>
</form>
{% endfor %}
</div>
{% endif %}
{% if form %}
</div>
<div class="column">
{% endif %}
<div class="box" id="order_info">
<h3>Order information</h3>
<dl>
<div>
<dt>Order opens</dt>
<dd>{{ order.starttime.strftime("%Y-%m-%d, %H:%M") }}</dd>
<dt>Order closes</dt>
<dd>
{% if order.stoptime %}
{% set stoptimefmt = (
"%H:%M" if order.stoptime.date() == order.starttime.date()
else "%Y-%m-%d, %H:%M"
) %}
{{ order.stoptime.strftime(stoptimefmt) }} ({{ order.stoptime|countdown }})
{% else %}
Never
{% endif %}
</dd>
</div>
<div>
<dt>Location</dt>
<dd>
{% if order.location %}
<a href="{{ url_for('general_bp.location', location_id=order.location_id) }}">{{ order.location_name }}</a>
{% else %}
{{ order.location_name }}
{% endif %}
</dd>
<dt>Courier</dt>
<dd>
{% if order.courier == None %}
{% if not current_user.is_anonymous() %}
<form action="{{ url_for('order_bp.volunteer', order_id=order.id) }}" method="post" style="display:inline">
<input type="submit" class="btn btn-primary btn-sm" value="Volunteer"></input>
</form>
{% else %}No-one yet{% endif %}
{% else %}
{{ order.courier.username }}
{% endif %}
</dd>
</div>
</dl>
<div>
{% if order.can_close(current_user.id) -%}
<form action="{{ url_for('order_bp.close_order', order_id=order.id) }}" method="post" style="display:inline">
<input type="submit" class="btn btn-danger" value="Close"></input>
</form>
{% endif %}
{% if courier_or_admin %}
<a class="btn" href="{{ url_for('order_bp.order_edit', order_id=order.id) }}">Edit</a>
{%- endif %}
</div>
</div>
<div class="box" id="how_to_order">
<h3>About {{ order.location_name }}</h3>
<dl>
{% if order.location.telephone %}
<div>
<dt>Telephone</dt>
<dd><a href="tel://{{ order.location.telephone }}">{{ order.location.telephone }}</a></dd>
</div>
{% endif %}
{% if order.location.website %}
<div>
<dt>Website</dt>
<dd><a href="{{ order.location.website }}">{{ order.location.website }}</a></dd>
</div>
{% endif %}
{% if order.location.address or order.location.osm %}
<div>
<dt>Location</dt>
<dd>
{% if order.location.osm %}
<a href="{{ order.location.osm }}">{{ order.location.address or "View on OSM" }}</a>
{% else %}
{{ order.location.address }}
{% endif %}
</dd>
</div>
{% endif %}
</dl>
</div>
{% if not form %}
</div>
<div class="column">
{% endif %}
<div class="box" id="per_dish">
<h3>Ordered dishes</h3>
{% for dish_name, dish_order_items in order.group_by_dish() -%}
{% set has_comments = dish_order_items | map(attribute="comment") | any -%}
<div class="dish {{ 'spacecake no_comments' if not has_comments }}">
<h4>
<span class="quantity">{{ dish_order_items | length }}</span> ×
{{ dish_name }}
</h4>
{% if has_comments -%}
<ul class="comments">
{% for item in dish_order_items -%}
<li class="spacecake">{% if item["comment"] %}<span>{{ item["comment"] }}</span>
{% else %}<i>No comment</i>
{% endif %}<span class="spacer"> </span><span class="item_for">for {{ item.for_name }}</span></li>
{% endfor %}
</ul>
{% else %}
<span class="spacer"> </span><span class="item_for">for {{ dish_order_items | map(attribute="for_name") | join(", ") }}</span>
{%- endif %}
</p>
</div>
{%- endfor %}
<div class="footer">
Total {{ order.items.count() }} items — {{ total_price|euro }}
&nbsp;
<a class="btn btn-sm" href="{{ url_for('order_bp.items_shop_view', order_id=order.id) }}">Shop view</a>
</div>
</div>
</div>
</section>
<section class="single_column">
<div class="box" id="per_person">
<h3>Items per person</h3>
<table class="table table-condensed">
<thead>
<tr><th>Total</th><th>Name</th><th>Items</th></tr>
</thead>
<tbody>
{% for user_name, order_items in order.group_by_user() -%}
<tr>
<td>
{% set paid = order_items | map(attribute="paid") | all %}
<input type="checkbox" name="{{ user_name }}"
{{ "disabled" if paid }} style="{{ 'opacity: 0.5' if paid }}">
<span class="price">{{ order_items | map(attribute="price") | sum | euro }}</span>
{% if paid %}paid{% endif %}
</td>
<td>{{ user_name }}</td>
<td class="items">
<ul>
{% for item in order_items %}
<li class="{{ 'paid' if item.paid }}">
<div class="actions">
{% if item.can_delete(order.id, current_user.id, session.get('anon_name', '')) -%}
<form action="{{ url_for('order_bp.delete_item', order_id=order.id, item_id=item.id) }}" method="post" style="display:inline">
<button class="btn btn-link btn-sm" type="submit" style="padding: 0 0.5em;"><span class="glyphicon glyphicon-remove"></span></button>
</form>
{% else %}
<span class="glyphicon glyphicon-remove" style="color: var(--gray3); padding: 0 0.5em"></span>
{%- endif %}
</div>
<div class="price_aligned">{{ item.price|euro }}</div>
<div class="item_description">{{ item.dish_name }}{{ "; " + item.comment if item.comment }}</div>
</li>
{% endfor %}
<li>
<button class="btn btn-link btn-sm" onclick="alert('TODO')" style="color: green; padding: 0 0.5em;"><span class="glyphicon glyphicon-plus"></span></button>
</li>
</ul>
</td>
</tr>
{%- endfor %}
</tbody>
</table>
<div class="footer">
On selected:
<button class="btn btn-sm"><span class="glyphicon glyphicon-ok"></span> Mark paid (TODO)</button>
<button class="btn btn-sm"><span class="glyphicon glyphicon-piggy-bank"></span> Tab (TODO)</button>
<button class="btn btn-sm"><span class="glyphicon glyphicon-qrcode"></span> QR code (TODO)</button>
</div>
</div>
</section>
{% endblock %}
{% block styles %}
{{ super() }}
<link rel="stylesheet" href="{{ url_for('static', filename='css/select2.min.css') }}" />
<link rel="stylesheet" href="{{ url_for('static', filename='css/select2-bootstrap.min.css') }}">
<link rel="stylesheet" href="{{ url_for('static', filename='css/print.css') }}">
<style>
body, h1, h2, h3, h4 {
color: var(--gray0);
}
table {
overflow-wrap: break-word;
}
@media (min-width: 1200px) {
section {
display: grid;
grid-template-columns: 1fr 1fr;
grid-gap: 0 30px;
align-items: start;
}
}
section.single_column {
grid-template-columns: unset;
}
.description {
margin-bottom: 10px;
}
header {
margin-bottom: 25px;
}
h2 {
margin-bottom: 0;
}
h3 {
margin-top: 0;
font-size: 150%;
font-weight: 500;
}
h4 {
margin-bottom: 0.5em;
font-size: 110%;
font-weight: 500;
}
.location {
font-size: 150%;
font-weight: 500;
margin-left: 3px;
margin-top: -5px;
}
.box {
width: 100%;
border: 2px solid var(--gray0);
padding: 10px;
margin-bottom: 30px;
}
@media (min-width: 500px) {
#order_info dl {
display: grid;
grid-template-columns: 1fr 1fr;
}
}
#from_favourites ul, #my_items ul, #per_person ul {
list-style-type: none;
padding: 0;
}
dl {
margin-bottom: 10px;
}
.box :last-child {
margin-bottom: 0;
}
.spacecake {
display: flex;
align-items: flex-end;
}
.spacecake .tags {
padding-left: 0.5em;
color: var(--gray2);
}
.spacecake .spacer {
content: ' ';
flex-grow: 1;
min-width: 10px;
border-bottom: 1px solid var(--gray3);
margin: 0.3em 0.5em;
}
li {
line-height: 1.5;
margin: 0.4em 0;
}
#my_items li form {
align-self: flex-start;
}
#per_dish ul {
margin-bottom: 0;
}
#per_dish h4 {
margin-top: 0;
}
#per_dish .dish.no_comments h4 {
margin-bottom: 0;
line-height: inherit;
}
#per_dish .dish {
margin-bottom: 13px;
}
#per_dish .item_for {
color: var(--gray2);
text-align: right;
}
#per_person li {
white-space: nowrap;
margin-top: 0.15em;
vertical-align: top;
}
#per_person li > * {
display: inline-block;
vertical-align: top;
}
#per_person .item_description {
white-space: normal;
}
.price, .price_aligned {
white-space: nowrap;
}
.price_aligned {
display: inline-block;
width: 4em;
}
.items .paid {
opacity: 0.4;
}
.footer {
margin-top: 1em;
}
summary {
line-height: 1.2;
margin: 0 -10px;
padding: 4px 10px;
}
summary .dish_name, summary:before {
align-self: flex-start;
}
details[open] summary .dish_name {
font-weight: bold;
}
details {
margin: 0 -10px;
padding: 0 10px;
}
details[open] {
background-color: var(--gray5);
padding-bottom: 5px;
}
details:not([open]) summary:hover {
background-color: var(--gray5);
}
.select2-container--default .select2-selection--multiple .select2-selection__rendered {
padding: 0 3px;
}
.select2-container .select2-selection--multiple {
min-height: 20px;
line-height: 1;
}
.select2-container .select2-search--inline .select2-search__field {
margin-top: 3px;
}
.select2-container--default .select2-selection--multiple .select2-selection__choice {
margin-top: 4px;
}
.select2-container li {
margin: 0;
}
.select2-selection--multiple .select2-search.select2-search--inline .select2-search__field:not(:focus) {
border-color: transparent;
box-shadow: none;
}
</style>
{% endblock %}
{% block scripts %}
{{ super() }}
<script src="{{ url_for('static', filename='js/select2.min.js') }}"></script>
{% if form %}
<script type="text/javascript">
"use strict";
function openDish(detailsEl, open=null) {
let openAttr = detailsEl.getAttribute("open");
let isOpen = openAttr || openAttr == "";
$("details").removeAttr("open");
if (open === true || open === null && !isOpen) {
detailsEl.setAttribute("open", true);
$(detailsEl).find(".select").select2({"sorter": x => x.sort()});
let id = detailsEl.parentNode.getAttribute("id");
window.history.replaceState(null, "", `#${id}`);
} else {
window.history.replaceState(null, "",
`${window.location.origin}${window.location.pathname}`);
}
}
$("summary").on("click", e => { openDish(e.currentTarget.parentNode); return false; });
$(window).on("load", () => {
let m = window.location.hash.match(/^#dish_[a-z0-9_-]*$/);
if (m[0]) {
openDish($(m[0]).find("details")[0], open=true);
}
});
</script>
{% endif %}
{% endblock %}