{% extends "base.html" %} {% block title %}Inventory · foilstack{% endblock %} {% block status %}{{ totals.count }} in stock · {{ totals.market|money }} at market{% if totals.sold_rows %} · {{ totals.sold_rows }} sold, {{ totals.realised|money }} realised{% endif %}{% endblock %} {% block screen %} {# Nowrap for the reason the listing run is: at 1000px, the width the README animation is captured at, the status chips pushed the primary button onto a row of its own and the facet row below it then read as four rows of chrome above the table. The give is the search box, which shrinks. #}

Inventory

{# The filters and the sort, carried through, so searching narrows what is on screen rather than throwing the rest of the controls away. #} {% for name, value in carry %}{% endfor %}
{% for c in status_chips %} {{ c.label }} {{ c.count }} {% endfor %}
{# Three numbers answering three questions: where this page sits, how many lines the filters matched, and how many there are in all. Collapsed to one they contradict each other — "100 lines" above a filter chip reading 4,231 reads as the filter having lost most of the inventory. The window is a separate clause rather than another "of". Written "1–100 of 138 of 152 lines" the sentence has two of them, and the middle number stops being readable as either bound. #} {# `data-plain` is the same label without the window, for when a selection is active. The bar is `bar-nowrap` and its only give is the search box, which by this point has already shrunk to its minimum — so all three clauses at once truncate to "740 cards …" and the number the seller most needs is the one that gets cut. The window is on the pager and in the selection bar by then, and the selection is not anywhere else. #} {% if last_page > 1 %}showing {{ row_from }}–{{ row_to }} · {% endif %}{{ matched_lines }}{% if narrowed %} of {{ total_lines }}{% endif %} lines
{# Disabled rather than hidden, unlike the Delete button beside it. That one is absent at rest because it is destructive and there is nothing to mis-click; this one is the only thing on the screen that says selecting rows leads anywhere, so it has to stay visible to be the instruction. It starts disabled in the markup rather than waiting for `sync()`, because a live primary button for one parse is one a fast hand can press — and until this it submitted no ids at all, which `/listings` reads as the whole inventory and prices a run over everything the account owns. The filter the seller was looking at is not on that form, so the screen they left said 75 unlisted and the one they arrived at answered for all of it. #}
{% if facets %}
{% for f in facets %}
{{ f.label }} {% for o in f.options %} {{ o.label }} {{ o.count }} {% endfor %} {% if f.hidden %}+{{ f.hidden }}{% endif %}
{% endfor %} {% if filtered %} clear {{ filtered }} filter{{ '' if filtered == 1 else 's' }} {% endif %}
{% endif %}
{# The filter this screen is showing, carried onto the run. A selection is not always a list of ids any more. One page of a hundred lines is around 740 copies, which is a 6.4 KB querystring — already inside what a proxy will commonly accept, before a shop with twenty copies of a staple is considered — and "everything matching" would be a quarter of a megabyte. So the filter travels instead and `/listings` resolves it through the same `inventory.narrow` this screen used. `sel` is what says which reading applies, and it is empty at rest. An absent mode selects the ids and nothing else: `/listings` once read "no ids" as "everything the account owns", with the filter nowhere on the form, so a screen reading `Not listed 75` handed off a run priced over the whole inventory. Breadth has to be something asked for. #} {% for name, value in sel_fields %}{% endfor %} {% if last_page > 1 %} {# Only where there is more to select than is on screen. On a single page "select all matching" and the header checkbox are the same act, and offering both invites the seller to look for a difference. #} {% endif %} {# Says which rows it means, because the table is a page of a larger result and the box cannot reach past it. A select-all that silently covers a hundred of four thousand lines is the whole hazard of paging a screen that feeds a listing run. #} {# A sortable heading is a link, not a script: the order is in the URL, so a sorted view is one a seller can bookmark and send. #} {% for r in rows %} {% endfor %}
{{ sorts.name|sortlink('CARD') }} {{ sorts.set|sortlink('GAME / SET') }} {{ sorts.quantity|sortlink('QTY') }} CONDITION PRINTING {{ sorts.cost|sortlink('COST') }} {{ sorts.market|sortlink('MARKET') }} {{ sorts.margin|sortlink('MARGIN') }} {{ sorts.listed|sortlink('LISTED') }}
{# One hidden input per copy, so the server contract stays "a list of inventory ids" no matter how the UI groups them. #} {% for i in r.ids %}{% endfor %} {# The catalogue image, not a scan: with several copies behind this line, picking one of their scans to represent it is arbitrary. #} {{ r.name }} {% if r.sold %}sold {% elif r.sold_quantity %}{{ r.sold_quantity }} sold{% endif %} {{ r.game }}{% if r.set_name %} / {{ r.set_name }}{% endif %} {{ r.quantity }} {{ r.conditions }} {{ r.printing_label }} {# Ahead of the "guessed" mark: a finish with no price behind it is the worse of the two and stays quiet on a single-printing card, which is most of the cards it happens to. "No price", not "no printing". The catalogue names printings it has no market figure for, and a foil printing nobody will pay for leaves this row priced off the non-foil just as surely as no foil printing at all would. Reading those two as the same fact is what this pill was doing, and it went quiet on the cards it was written for. #} {% if r.finish_unpriced %}! {% elif r.guessed %}?{% endif %} {{ r.cost|money if r.cost is not none else '—' }} {{ r.market|money if r.market is not none else '—' }} {{ r.margin_pct ~ '%' if r.margin_pct is not none else '—' }} {{ r.listed_label }}
{% if not rows %}
{% if show == 'printing' %}every card is priced on a printing you chose {% elif filtered and total_lines %}no card matches every filter · clear them {% elif q %}nothing matches “{{ q }}” {% else %}nothing confirmed yet · import some scans to get started{% endif %}
{% endif %}
{# Inside the scroller and after the table, so it sits at the end of the rows rather than floating in the chrome. Absent entirely on a single page: a paging control that cannot page is furniture. #} {% if last_page > 1 %} {% endif %}
{% endblock %} {% block scripts %} {% endblock %}