haldis/app/templates/order.html
Midgard 105cf4a044
Revert "Don't crash on orders that don't have a slug"
This reverts commit ff0ea068de.

The next commit (Create a slug for old orders in the migration) will
change our strategy of writing code to handle the legacy slugless orders:
we will generate a slug for old orders.
2022-05-25 13:56:07 +02:00

590 lines
16 KiB
HTML
Raw Permalink 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">
<script type="text/javascript" src="{{ url_for('static', filename='js/jquery.min.js') }}"></script>
<script type="text/javascript" src="{{ url_for('static', filename='js/qrcode.min.js') }}"></script>
{% endblock %}
{% block container %}
<header class="row">
<div class="col-md-2" style="padding-top: 2em">
<div id="qrcode"></div>
<script type="text/javascript">
var qrcode = new QRCode(document.getElementById("qrcode"), {
text: "{{ url_for("order_bp.order_from_slug", order_slug=order.slug, _external=True) }}",
width: 128,
height: 128,
colorDark : "#000000",
colorLight : "#ffffff",
correctLevel : QRCode.CorrectLevel.H
});
</script>
</div>
<div class="col-md-10">
<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>
<div>
Unique order link: <code>{{ url_for("order_bp.order_from_slug", order_slug=order.slug, _external=True) }}</code>
</div>
</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_slug=order.slug, 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_slug=order.slug) }}" 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_slug=order.slug) }}" 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_slug=order.slug) }}" 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_slug=order.slug) }}">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_quantity, dish_comment_groups in order.group_by_dish() -%}
{% set has_comments = dish_comment_groups | length > 1 or (dish_comment_groups | map("first") | any) -%}
<div class="dish {{ 'spacecake no_comments' if not has_comments }}">
<h4>
<span class="quantity">{{ dish_quantity }}</span> ×
{{ dish_name }}
</h4>
{% if has_comments -%}
<ul class="comments">
{% for comment, items in dish_comment_groups -%}
<li class="spacecake"><span class="comment">
<span class="quantity">{{ items | length }}</span> ×
{% if comment %}{{ comment }}
{% else %}<i>No comment</i>
{% endif %}</span><span class="spacer"> </span><span class="item_for">for {{ items | map(attribute="for_name") | join(", ") }}</span></li>
{% endfor %}
</ul>
{% else %}
<span class="spacer"> </span><span class="item_for">for {{ dish_comment_groups[0][1] | 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_slug=order.slug) }}">Shop view</a>
</div>
</div>
</div>
</section>
<section class="single_column">
<div class="box" id="per_person">
<h3>Items per person</h3>
<form action="{{ url_for('order_bp.modify_items', order_slug=order.slug) }}" method="post">
<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_names" value="{{ user_name }}"
{{ "disabled" if not order.can_modify_payment(current_user.id) }}>
<span class="price" style="{{ 'opacity: 0.5' if paid }}">
{{ order_items | map(attribute="price") | ignore_none | sum | euro }}
</span>
{% if paid %}<span class="glyphicon glyphicon-ok" style="opacity: 0.5"></span>{% endif %}
</td>
<td style="{{ 'opacity: 0.5' if paid }}">{{ 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_slug=order.slug, 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; cursor: not-allowed"></span>
{%- endif %}
</div>
<div class="price_aligned">
{{ item.price|euro }}
{% if item.price_modified %}
<span class="glyphicon glyphicon-pencil" style="opacity: 0.5" title="Edited"></span>
{% endif %}
</div>
<div class="item_description">{{ item.dish_name }}{{ "; " + item.comment if item.comment }}</div>
</li>
{% endfor %}
</ul>
</td>
</tr>
{%- endfor %}
</tbody>
</table>
<div class="footer">
{% if order.can_modify_payment(current_user.id) %}
On selected:
<button name="action" value="mark_paid" class="btn btn-sm"><span class="glyphicon glyphicon-ok"></span> Mark paid</button>
<button name="action" value="mark_unpaid" class="btn btn-sm">Mark unpaid</button>
{% endif %}
{% if order.can_modify_prices(current_user.id) %}
&nbsp; <span style="border-left: 1px solid var(--gray0); display: inline-block;">&nbsp;</span>&nbsp;
<a href="{{ url_for('order_bp.prices', order_slug=order.slug) }}" class="btn btn-sm">
<span class="glyphicon glyphicon-pencil"></span> Edit prices
</a>
{% endif %}
</div>
</form>
</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;
}
.main 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_dish .comments {
padding-left: 1.5em;
list-style-type: none;
}
#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 %}