add /tu/{proposal_id} (get, post) routes

This commit ports the `/tu/?id={proposal_id}` PHP routes to
FastAPI into two individual GET and POST routes.

With this port of the single proposal view and POST logic,
several things have changed.

- The only parameter used is now `decision`, which
  must contain `Yes`, `No`, or `Abstain` as a string.
  When an invalid value is given, a BAD_REQUEST response
  is returned in plaintext: Invalid 'decision' value.
- The `doVote` parameter has been removed.
- The details section has been rearranged into a set
  of divs with specific classes that can be used for
  testing. CSS has been added to persist the layout with
  the element changes.
- Several errors that can be discovered in the POST path
  now trigger their own non-200 HTTPStatus codes.

Signed-off-by: Kevin Morris <kevr@0cost.org>
This commit is contained in:
Kevin Morris 2021-06-19 05:08:25 -07:00
parent 83c038a42a
commit 85ba4a33a8
7 changed files with 571 additions and 2 deletions

View file

@ -0,0 +1,106 @@
<h2>{% trans %}Proposal Details{% endtrans %}</h2>
{% if voteinfo.is_running() %}
<p class="vote-running" style="font-weight: bold; color: red">
{% trans %}This vote is still running.{% endtrans %}
</p>
{% endif %}
<!-- The margin style here mimics the margin on the old <p> element. -->
<div class="proposal details">
<div class="field user">
{{ "User" | tr }}:
<strong>
{% if voteinfo.User %}
<a href="{{ '/packages/?K=%s&SeB=m' | format(voteinfo.User)}}">
{{ voteinfo.User }}
</a>
{% else %}
N/A
{% endif %}
</strong>
</div>
{% set submitted = voteinfo.Submitted | dt | as_timezone(timezone) %}
{% set end = voteinfo.End | dt | as_timezone(timezone) %}
<div class="field submitted">
{{
"Submitted: %s by %s" | tr
| format(submitted.strftime("%Y-%m-%d %H:%M"),
voteinfo.Submitter.Username | e)
}}
</div>
<div class="field end">
{{ "End" | tr }}:
<strong>
{{ end.strftime("%Y-%m-%d %H:%M") }}
</strong>
</div>
{% if not voteinfo.is_running() %}
<div class="field result">
{{ "Result" | tr }}:
{% if not voteinfo.ActiveTUs %}
<span>{{ "unknown" | tr }}</span>
{% elif accepted %}
<span style="color: green; font-weight: bold">
{{ "Accepted" | tr }}
</span>
{% else %}
<span style="color: red; font-weight: bold">
{{ "Rejected" | tr }}
</span>
{% endif %}
</div>
{% endif %}
</div>
<div class="proposal agenda">
<p class="field agenda">
<!-- The `e` filter manually escapes content. -->
{{ voteinfo.Agenda | replace("\n", "<br />\n") | safe | e }}
</p>
</div>
<table class="vote-status">
<tr>
{% if not voteinfo.is_running() %}
<th>{{ "Yes" | tr }}</th>
<th>{{ "No" | tr }}</th>
<th>{{ "Abstain" | tr }}</th>
{% endif %}
<th>{{ "Total" | tr }}</th>
<th>{{ "Voted" | tr }}</th>
<th>{{ "Participation" | tr }}</th>
</tr>
<tr>
{% if not voteinfo.is_running() %}
<td>{{ voteinfo.Yes }}</td>
<td>{{ voteinfo.No }}</td>
<td>{{ voteinfo.Abstain }}</td>
{% endif %}
<td>{{ voteinfo.total_votes() }}</td>
<td>
{% if not has_voted %}
<span style="color: red; font-weight: bold">
{{ "No" | tr }}
</span>
{% else %}
<span style="color: green; font-weight: bold">
{{ "Yes" | tr }}
</span>
{% endif %}
</td>
<td>
{% if voteinfo.ActiveTUs %}
{{ (participation * 100) | number_format(2) }}%
{% else %}
{{ "unknown" | tr }}
{% endif %}
</td>
</tr>
</table>

View file

@ -0,0 +1,14 @@
<form class="action-form" action="/tu/{{ proposal }}" method="POST">
<!-- Translate each button's text but leave the value alone. -->
<fieldset>
<button type="submit" class="button" name="decision" value="Yes">
{{ "Yes" | tr }}
</button>
<button type="submit" class="button" name="decision" value="No">
{{ "No" | tr }}
</button>
<button type="submit" class="button" name="decision" value="Abstain">
{{ "Abstain" | tr }}
</button>
</fieldset>
</form>

View file

@ -0,0 +1,10 @@
<h2>{{ "Voters" | tr }}</h2>
<ul id="voters">
{% for voter in voters %}
<li>
<a href="/account/{{ voter.Username | urlencode }}">
{{ voter.Username | e }}
</a>
</li>
{% endfor %}
</ul>

20
templates/tu/show.html Normal file
View file

@ -0,0 +1,20 @@
{% extends "partials/layout.html" %}
{% block pageContent %}
<div class="box">
{% include "partials/tu/proposal/details.html" %}
</div>
<div class="box">
{% include "partials/tu/proposal/voters.html" %}
</div>
<div class="box">
{% if error %}
<span class="status">{{ error | tr }}</span>
{% else %}
{% include "partials/tu/proposal/form.html" %}
{% endif %}
</div>
{% endblock %}