offer_checkout and auction_checkout were creating a cart and hand-setting
cart.active = True, which left the user with two active carts for the
shop; /cart then resolved to the older, empty one — so "Pay $X" appeared
to do nothing. Both now use shop.create_new_cart_for_user() (deactivates
the user's other carts for that shop, activates the new one) and redirect
to /cart/{cart_id} directly, so it works regardless of which shop the
request is scoped to.
Tests: TestOfferCheckout / TestAuctionCheckout now assert there is exactly
one active cart, it carries the cart_offer/cart_auction association, it is
non-empty, and the redirect targets that cart by id. 990 passed.
These end the negotiation, so the forms now carry onsubmit="return
confirm(...)". offer.js bails when the prompt is cancelled — it checks
event.defaultPrevented before firing the AJAX request — so the JS path
respects the confirmation too, and the no-JS path gets the native dialog
before the POST+redirect.
Crossfades (user-initiated and DJ auto-transitions) faded the incoming
track up to volume 1.0 regardless of how loud the playing tab actually
was, producing a jarring jump in loudness mid-transition. Read
activeMedia.volume when a fade begins and use it as the ceiling for both
the outgoing and incoming tracks; restore that same level if a DJ
crossfade is cancelled.
The previous .action-button-grid used auto-fit/minmax(15rem,1fr) which
produced four columns on a wide well, and .mps-button's `min-width: 100%`
+ auto margins fought grid track sizing so the gap collapsed and buttons
touched. Now: single column on narrow, exactly two equal columns at
≥720px (auto-flow keeps them balanced), explicit column/row gap, and the
grid resets .mps-button min-width/margins. The "🤝 Offers" button is now
always shown to shop owners (was gated on shop.offer_enabled) so the
offers inbox is always reachable.
- Offer history & offer page show the buyer's display name (User.display_name
= the public `name` handle; `full_name` is private) linked to a profile
page — never the email. _serialize_offer drops buyer_email; events carry
actor_name/actor_handle/actor_id, header carries buyer_name/buyer_handle.
- New public profile page: GET /profile/{handle} (views/user.py:user_profile,
template profile.j2). Shows gravatar (User.gravatar_url(size) — forced
identicon unless the user opted into Gravatar), member-since, owned/edited
shops, and a <details> "Show email" that is server-gated: only the user
themselves, or a shop owner/editor viewing in that shop's context
(?shop={shop_id}) when the profile user has transacted there (an offer or
an invoice).
- New operator offers inbox: GET /s/{shop_id}/offers (@shop_editor_required,
shop_offers.j2) — open offers first, each row links to /o/{id} and the
buyer's profile. Reachable from /actions/view via a new "Offers" button
(shown when shop.offer_enabled).
- /actions/view rebuilt: one flat .action-button-grid (Grid auto-fit,
minmax(15rem,1fr)) inside a properly-padded .action-columns well — fixes
the off-balance two-column layout and buttons overflowing the well; no
<br> spacers. Styleguide gains profile-card and action-button-grid
patterns.
- offer.j2: buyer name shown (linked to profile); "Buyer:"/"Seller:" message
lines renamed "Buyer note:"/"Seller note:" to disambiguate.
Tests: 11 new functional tests (profile render + email gating, offers inbox,
actions button, styleguide). 989 passed.
- offer.j2 renders a state-aware notice (.offer-state-notice, .alert
variants) above the action forms: declined / withdrawn / expired /
accepted (+ pay-now hint for buyer) / your-turn / waiting — so the
viewer always understands the offer state without depending on a flash
a JS redirect would skip. _serialize_offer now exposes is_declined,
is_withdrawn, is_expired, is_pending, is_countered, is_accepted.
- Shop-settings Make-an-Offer section restyled with the new
.settings-form / .settings-form-grid / .settings-field /
.settings-field-hint system (two-up grid, per-field hints, submit
pinned right). Added a styleguide entry under #forms.
- Reworded the section blurb: auto-declined offers are NOT silent — the
buyer is told their offer was too low; only the seller isn't pinged.
- Fixed --color-text-muted typo (→ --text-muted) on .offer-js-flash-info.
Tests: TestOfferRoutes gains 3 state-notice render tests;
TestSettingsFormStyleguide covers the styleguide + live shop-settings
markup and asserts the old wording is gone. 978 passed.
Every bid/buy-now/watch and offer open/counter/accept/decline/withdraw
POST now works as a plain browser submit: flash + 302 redirect to the
auction/offer page. JSON is returned only when the request carries
X-Requested-With: XMLHttpRequest. Adds offer.js progressive-enhancement
layer (mirrors auction.js); pay-now CTA on accepted offers; .offer-js-flash
styling; grid layout for offer/action forms. offer_accept emails the
buyer only on the transition into ACCEPTED.
Tests: TestOfferRoutes/TestAuctionRoutes now drive the JSON path via an
AJAX helper; new TestOfferNoJsFallback/TestAuctionNoJsFallback cover the
plain-POST redirect path. 973 passed.
All transactional mail now sends From app.email.sender (default
no-reply@origin.makepostsell.com) instead of per-shop no-reply@<domain>,
with the shop name (or email.from_name) as the display name. The origin
identity is DKIM-signed (d=makepostsell.com) and SPF-authorized and
relays via mx1's warm IP, so operator custom-domain shops stop getting
spam-foldered. format_from_header() builds the From; send_email() gained
a from_name kwarg. Reply-To / per-shop contact email still TODO.
Per fox: "a touch more whitespace around the bid and optional message".
The expanded Make-an-offer form had the label, amount input, message
input, and submit button stacked flush against each other inside the
$42.00 well — no vertical rhythm.
New .product-offer-form CSS:
- display: grid; gap: var(--space-3, 12px)
- top margin var(--space-3) separates it from the summary button
- inputs get box-sizing: border-box, width: 100%, padding 8px 12px so
they fill the column with comfortable internal spacing
Grid only.
Per fox: the price / Add-To-Cart / Make-an-offer box gets squeezed too
narrow on mid-width viewports (960px–1200px) where the 2fr/1fr split
gives the 1fr column only ~250-300px, crushing the buttons.
section.two-column grid columns: minmax(0, 2fr) minmax(280px, 1fr)
- the images column can shrink (minmax(0, 2fr)) so the purchase
column always gets its 280px minimum first
- once the viewport is wide enough, the columns return to the 2:1
ratio up to the 1200px max-width
Grid only, no flex.
Tests pass (10 in target slices).
The inline-grid + place-items: center change put each child of a button
into its own grid row. Buttons that hold an icon span + label text (the
windows-95 hamburger: <span>☰</span> + "shop.unturf.com") got the icon
on one line and the label on the next.
.mps-button is now display: inline-block again — multi-child content
flows on one line as expected. The squish fix is retained without grid:
- box-sizing: border-box (normalizes <button> vs <a>)
- padding: 12px 18px (symmetric, generous horizontal — no more text
jammed against the edge)
- line-height: 1.4 (explicit, not UA "normal")
- min-height: 44px (tap-target floor)
- appearance: none, font-family: inherit
.mps-button-primary likewise back to inline-block with box-sizing,
line-height, text-align: center.
(.edit-card-icon stays inline-grid + place-items: center — it holds a
single glyph, so grid centering is correct there. Still Grid only,
no flex anywhere.)
Tests pass (10 in target slices).
I shipped display:inline-flex / display:flex across .mps-button,
.mps-button-primary, .edit-card-icon, edit-page h3 headers,
.edit-status-bar, .edit-status-pill, .edit-save-bar,
.upload-thumbnails-header, .upload-thumbnail-item, and an inline
style on the torrent_opt_in label — all violations of the project's
Grid-only rule. fox caught it. All converted:
- .mps-button / .mps-button-primary / .edit-card-icon:
display: inline-grid; place-items: center (was inline-flex + center)
- edit-page h3 headers (icon + title):
display: grid; grid-template-columns: auto 1fr; align-items: center
- .edit-status-bar: text-align: right + inline-grid pills that flow/wrap
(was flex + flex-wrap + justify-content: flex-end)
- .edit-status-pill: display: inline-grid; grid-auto-flow: column
- .edit-save-bar: display: grid; grid-template-columns: 1fr auto
- .upload-thumbnails-header: display: grid; grid-template-columns: 1fr auto
with a max-width: 600px media query collapsing to 1fr
- .upload-thumbnail-item: display: grid; grid-auto-rows: min-content;
align-content: space-between (replaces the flex margin-top: auto trick
for pinning the upload form to the bottom of the stretched cell)
- torrent_opt_in label inline style: display:grid;grid-template-columns:auto 1fr
CLAUDE.md updated: the CSS-layout rule now spells out the Grid
equivalent for every flex pattern, clarifies which alignment
properties ARE valid in grid context, and includes a dated SHAME LOG
entry for this transgression.
Tests pass (11 in target slices).
Browsers were caching /static/css/common.css indefinitely (the <link>
had no version param), so CSS fixes didn't show up until a hard
refresh. Fox kept seeing the old squished-button CSS after deploys.
- New request property request.git_hash (reified) returns the short
git hash baked in at deploy time (views.version.GIT_HASH)
- base.j2 tokens.css and common.css links now carry ?v={{ request.git_hash }}
Each deploy bumps GIT_HASH, so the CSS URL changes, so browsers fetch
the fresh file. No more stale-CSS confusion.
Per fox: Add To Cart / Make an offer buttons looked squished (text
crammed at top, little vertical space) while .mps-button on <a>
elements (cart page) looked fine.
Root cause: <button> elements inherit UA-stylesheet line-height and
box-sizing that differ from <a>. The class set padding-top/bottom: 14px
but with the native button line-height the text didn't center properly.
Normalized .mps-button:
- appearance: none (+ -webkit-) — strip native button chrome
- box-sizing: border-box — consistent across <button>/<a>/<input>
- font-family: inherit — buttons default to a different font
- line-height: 1.4 — explicit, was relying on UA "normal"
- padding: 12px 16px (was 14px 0) — symmetric, gives horizontal
breathing room too
- vertical-align: middle
Now <button class="mps-button">, <a class="mps-button">, and
<input type=submit class="mps-button"> all render the same.
Tests pass (9 in pricing_mode slice).
Per fox: "i only see add to cart not offer" — viewing their own
offer-mode (3/4) product as the shop owner showed just Add To Cart
with no sign that offers were active. The Make-an-offer form is
correctly hidden for owners (can't offer on your own product, same
as auctions), but that left owners with no feedback.
Now when product.offers_allowed and the viewer is a shop owner,
the page shows:
✓ Offers enabled — buyers see a "Make an offer" button here.
You can't offer on your own product.
Buyers (non-owner, logged in) still see the expandable offer form.
Anon visitors still see "Log in to make an offer".
Updated test_make_offer_hidden_for_seller: owner now asserts no
Submit Offer button + no "Log in to make an offer" CTA, but DOES
see "Offers enabled" indicator.
Root cause found via prod debug log: the product edit form's
onsubmit="submit.disabled = true" handler disables the submit button
before the browser collects form data, so the "submit" key is never
sent in the POST. The pricing_mode / allow_offers / auction-config
block was gated on `if "submit" in request.params:` — always False —
so it never ran. Hence no flash, no save.
The standard handlers (title, description, price, visibility) don't
have this gate; they check whether the field's value changed. The
pricing_mode block now follows the same pattern: gated on
`if "pricing_mode" in request.params:` (that radio only appears on
the product-title-and-description edit form, so other forms on the
page — uploads, inventory, bundle — don't trigger it).
- pricing_mode: read request.params["pricing_mode"], apply if valid
and changed; immediate flush; flash; auto-create draft auction when
switching into auction mode
- allow_offers: only processed when "allow_offers" in request.params
(so a different form submitting won't reset the override)
- auction config block: unchanged, still inside the pricing_mode gate
- torrent_opt_in: also re-gated on "pricing_mode" in params instead of
"submit" (was equally broken, just masked by the torrent kill switch)
Removed all the temporary debug logging (ENTER line, /opt and /tmp
file writes, journalctl warning).
Tests pass (15 in pricing_mode + auction-config slices).
The /tmp/mps-pricing-debug.log file didn't appear after fox triggered
a save attempt. Two changes to narrow down why:
1. Move the debug log to /opt/make_post_sell/pricing-debug.log (where
uwsgi has owner write access) with /tmp as fallback. PrivateTmp or
permissions might be blocking /tmp writes on this systemd setup.
2. Add an ENTER log line at the top of product_edit() that fires for
every request — GET, POST, save, etc. If this line never appears,
the view itself isn't being reached and the form is going somewhere
else. If it appears but the pricing_mode line doesn't, the submit
field is missing from params.
Layout restructure per fox:
- "Edit Title, Description, or Visibility" rendered first at top of the
edit grid (full width) via CSS order: 1. Useful info (title, price,
pricing mode, visibility) is now the primary focus when landing on
the page.
- "Upload or Replace Preview File" extracted into its own <section
class="upload-preview well2"> sibling. It sits in the right column
of the same row as "Upload or Replace Product File" via the natural
2-col grid flow.
- Visual order: title-and-description → upload-product → upload-preview
→ upload-thumbnails → price-history. Implemented via CSS order
property so HTML structure stays simple.
- Preview section only renders for digital sellable products (not bundle,
not physical) — same guard as before but now an explicit conditional
around the new <section>.
Debug:
- pricing_mode handler now ALSO writes to /tmp/mps-pricing-debug.log
(world-writable) in addition to the journalctl warning. Lets us read
POST params on prod without sudo. Will be removed when bug is fixed.
Tests still pass (9 pricing_mode tests).
Per fox: status data was "in the wrong spot" — it had been rendered at
the top of the content area as a standalone full-width row above the
edit-page grid. Moved into the call_to_action block (top-right of the
page, same area where the permanent link lives) so the at-a-glance
state sits naturally above the form rather than splitting visual flow.
- Pills (visibility / ready / price) and permanent link now render
together in call_to_action
- .edit-status-bar restyled: no background, no border, right-justified
flex row (matches the right-aligned page header area)
- .edit-permanent-link: right-aligned, muted, small
Tests still pass (9 in target slice).
Per fox: thumbnails "not aligned properly" and "not using horizontal
space properly". Two issues:
1) Grid was at minmax(240px, 1fr) which on the full-card-width row
often fit 3 columns instead of 4 (240 * 4 + gaps > available
space). Lowered to minmax(200px, 1fr) so the auto-fit gives 4
columns on a standard desktop edit card.
2) Each upload-thumbnail-item now renders as a sub-card:
- flex-direction: column with consistent gap
- background --surface-dim, --color-border, --radius-md
- preview image fixed 140px height + object-fit: contain so all
thumbnails align even when some slots are empty or have different
aspect ratios
- form pushed to bottom via margin-top: auto, so the upload buttons
line up across cells regardless of how much text is above
- p.bold-text break-word so long filenames don't blow out the cell
- the legacy <hr/> separator between items is hidden inside the grid
(it was a vertical-flow artifact)
The grid uses align-items: stretch so all 4 cells have equal height.
Tests still pass (9 in target slice).
Per fox: the thumbnails card layout was a mess — the h3, descriptor
text, and thumbnail items were all crammed into a single grid with
auto-fit columns, so thumbnail 1+2 sat on the same row as the header
and thumbnails 3+4 wrapped to row 2 leaving the left half empty.
Fix: restructure to a proper header + grid:
<section class="upload-thumbnails well2 edit-card-full">
<div class="upload-thumbnails-header">
<h3>... icon + title ...</h3>
<p class="upload-thumbnails-hint">Your cover ...</p>
</div>
<div class="upload-thumbnails-grid">
<div class="upload-thumbnail-item">...</div>
... (4 thumbnails)
</div>
</section>
CSS:
- .upload-thumbnails-header: flex row, space-between, wraps on narrow
screens; h3 grows, hint right-aligned + max-width 320px
- .upload-thumbnails-grid: auto-fit minmax(240px, 1fr) for the 4
thumbnail slots, gap --space-4
Result: header reads cleanly across the top with the cover descriptor
on the right, and all 4 thumbnails sit in a clean grid below.
Tests still pass (9 in target slice).
Per fox: "🎨 Upload or Replace Thumbnail Files ... should be in a row
that spans two columns" — and the same for "Edit Title, Description,
or Visibility".
Both cards now get class edit-card-full, which sets grid-column: 1 / -1
so they span the entire row in the 2-column edit grid. The thumbnail
grid has up to 4 thumbnails that need room; the edit-details card
has a wide form with pricing-mode radios and description textarea.
New CSS:
div.edit-page > section.edit-card-full { grid-column: 1 / -1; }
Styleguide updated with a live half/half/full example demonstrating
the pattern.
Per fox: "make this look awesome", "use styleguide", "think material
design to make it stand out as to what to do".
Status bar at top of edit page — three pills surface the product's
current state in one glance:
- Visibility: Public (blue) / Unlisted (purple) / Private (gray)
- Ready to sell (green) / Missing required files (gold warning)
- Current price (gray)
The pills tell the owner immediately what's done and what isn't.
Section cards (.well2 on edit page) get Material-card treatment:
- Background, rounded corners, border, elevation-1 shadow
- Hover/focus-within bumps to elevation-3 (subtle lift)
- h3 header gets a 36px circular icon badge (.edit-card-icon),
thin bottom border separating header from content
- Icons: 📁 product file, 📺 preview, 🎨 thumbnails, ✏️ details,
📦 inventory, 📦 bundle
Primary save action: Save Settings button is now a filled,
prominent CTA (navy bg, white text, bold, elevation-1 shadow that
lifts to elevation-2 on hover, disabled state dims). Class
.mps-button-primary on the styleguide for reuse.
.edit-save-bar provides a sticky-bottom save container CSS for a
follow-up wrapper around the Save Settings input (not yet wired
into product_edit.j2 — same hash adds the CSS so it's available).
All wired into the styleguide:
- Status bar + 3 pill states (public ready, private incomplete, etc.)
- Section card with icon header (3 example cards)
- Primary save action button
Tokens used (with fallbacks for safety): --elevation-1/3, --radius-lg,
--radius-pill, --color-navy/navy-dark, --color-blue-tint, --space-N,
--motion-fast, --color-green-dark, --color-gold, --color-purple.
Tests still pass (10 in target slice).
Per fox's feedback on shop.unturf.com edit page:
- File Type and File Size now stack on a single line with · separator
(was 2 stacked <p> tags). Class file-meta on the wrapper, file-meta-item
on each child. Applied to product, preview, thumbnail blocks.
- File Statistics removed as a standalone well; "Total capacity" line
moved into the upload-product well at top, using the same file-meta
pattern. Eliminates the half-empty 4th column in the 2-col grid.
- Thumbnail upload sections now have an <h4 class="thumbnail-label">
showing thumbnail1 / thumbnail2 / etc. so owners can tell which slot
is the cover (thumbnail1 shows on search pages) vs additional images.
- Description label split fix: the label, helper text, and textarea
are now properly stacked. The section.product-title-and-description
rule (display: grid for product page) is overridden to display: block
on the edit page so the form flows normally.
- Helper text under the Description label uses <small class="form-help">
with token-based styling.
- Textarea max-width 600px lifted on the edit page so it fills the
available well width.
Styleguide entries added at /styleguide for: file-meta line, thumbnail
label, form-help.
Tests still pass (10 in target slice).