5.5 KiB · text History 6dd7389
{# The live part of an outing. While open it re-fetches itself on the SSE
   `changed` event (connected by the wrapper in show.html, which isn't
   swapped, so the stream survives), with a slow poll as a backstop. Buttons
   inside inherit hx-target/hx-select/hx-swap from here, so each action
   returns a fresh panel too. #}
<div id="outing-panel" class="space-y-4"
     hx-target="#outing-panel" hx-select="#outing-panel" hx-swap="outerHTML"
     {% if outing.status == "open" %}
     hx-get="/outings/{{ outing.id }}/panel" hx-trigger="sse:changed, every 30s"
     {% endif %}>

  <div class="flex items-center justify-between gap-3 flex-wrap">
    <p class="text-sm">
      <span class="opacity-70">Going:</span>
      {% if going %}{{ going | join(", ") }}{% else %}<span class="opacity-70">nobody yet</span>{% endif %}
    </p>
    {% if outing.status == "open" %}
      {% if am_going %}
        <button type="button" class="btn btn-ghost btn-xs" hx-post="/outings/{{ outing.id }}/leave">
          <i class="fas fa-person-walking-arrow-right"></i> I'm out
        </button>
      {% else %}
        <button type="button" class="btn btn-primary btn-sm" hx-post="/outings/{{ outing.id }}/join">
          <i class="fas fa-hand"></i> I'm in
        </button>
      {% endif %}
    {% endif %}
  </div>

  {% if outing.status == "decided" %}
    <div class="card bg-success/10 border border-success p-5 text-center space-y-1">
      <p class="text-xs uppercase tracking-widest opacity-70">Decided</p>
      {% if decided %}
        <a href="/restaurants/{{ decided.id }}" class="text-2xl font-bold link link-hover">{{ decided.name }}</a>
        {% if decided.address %}<p class="text-sm opacity-70">{{ decided.address }}</p>{% endif %}
      {% endif %}
      <p class="text-xs opacity-70">Logged to <a class="link" href="/areas/{{ area.id }}/history">{{ area.name }}'s history</a>.</p>
    </div>
  {% elif outing.status == "cancelled" %}
    <div class="alert text-sm"><i class="fas fa-ban"></i> This lunch was called off.</div>
  {% elif options | length == 0 %}
    <div class="alert alert-warning text-sm">
      <span>Nothing to vote on: everything in {{ area.name }} is vetoed by someone going or closed at {{ outing.meal_time[:5] }}, or there are no restaurants yet.
      <a class="link" href="/areas/{{ area.id }}/restaurants/new">Add some</a>.</span>
    </div>
  {% else %}
    <p class="text-xs opacity-70">
      Vote for every option you'd happily go to. Re-roll swaps only the options nobody voted for.
    </p>
    <ul class="space-y-2">
      {% for o in options %}
      {% set leading = o.votes > 0 and o.votes == leader_votes %}
      <li class="card bg-base-200 border {{ 'border-success' if leading else 'border-base-300' }} p-4">
        <div class="flex items-start justify-between gap-3 flex-wrap">
          <div class="min-w-0">
            <a href="/restaurants/{{ o.restaurant.id }}" class="font-bold text-lg link link-hover">{{ o.restaurant.name }}</a>
            {% if leading %}<span class="badge badge-success badge-sm align-middle">leading</span>{% endif %}
            {% if outing.rolls > 1 and o.roll == outing.rolls %}<span class="badge badge-info badge-sm align-middle">new</span>{% endif %}
            <p class="text-xs opacity-70">
              {{ [o.restaurant.cuisine, o.restaurant.address] | select | join(" · ") }}
              {%- if o.restaurant.cuisine or o.restaurant.address %} · {% endif %}{{ (o.probability * 100) | round | int }}% odds when drawn
            </p>
            {% if o.vetoed_by %}
              <p class="text-xs text-error mt-1"><i class="fas fa-ban"></i> {{ o.vetoed_by | join(", ") }} ranked this "never" (joined after it was drawn)</p>
            {% endif %}
            <p class="text-sm mt-1">
              <span class="font-mono">{{ o.votes }}</span> vote{{ "" if o.votes == 1 else "s" }}{% if o.voters %}: {{ o.voters | join(", ") }}{% endif %}
            </p>
          </div>
          <div class="flex gap-2 shrink-0">
            <button type="button" class="btn btn-sm {{ 'btn-success' if o.mine else 'btn-outline' }}"
                    aria-pressed="{{ 'true' if o.mine else 'false' }}"
                    hx-post="/outings/{{ outing.id }}/vote" hx-vals='{"restaurant_id": "{{ o.restaurant.id }}"}'>
              <i class="fas {{ 'fa-check' if o.mine else 'fa-thumbs-up' }}"></i> {{ "Voted" if o.mine else "Vote" }}
            </button>
            <button type="button" class="btn btn-sm btn-ghost"
                    hx-post="/outings/{{ outing.id }}/decide" hx-vals='{"restaurant_id": "{{ o.restaurant.id }}"}'
                    hx-confirm="Go to {{ o.restaurant.name }}? This logs the lunch for everyone going.">
              <i class="fas fa-flag-checkered"></i> Go here
            </button>
          </div>
        </div>
      </li>
      {% endfor %}
    </ul>
  {% endif %}

  {% if outing.status == "open" %}
  <div class="flex items-center justify-between gap-3 flex-wrap">
    <div class="flex items-center gap-3">
      <button type="button" class="btn btn-sm" hx-post="/outings/{{ outing.id }}/reroll"
              {% if all_voted %}disabled title="Every option has a vote, so there's nothing to swap"{% endif %}>
        <i class="fas fa-dice"></i> Re-roll the rest
      </button>
      <span class="text-xs opacity-70">
        roll {{ outing.rolls }}{% if rolled_away %} · {{ rolled_away }} rolled away{% endif %}
      </span>
    </div>
    <button type="button" class="btn btn-ghost btn-xs" hx-post="/outings/{{ outing.id }}/cancel"
            hx-confirm="Call off this lunch?">
      Call it off
    </button>
  </div>
  {% endif %}
</div>