MPS-21: public profile page, offer-history identity, shop offers inbox, actions hub rebuild
- 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.
This commit is contained in:
parent
cb61227b2c
commit
e97d18bccd
14 changed files with 654 additions and 68 deletions
|
|
@ -232,7 +232,7 @@ All components are documented with live examples at `/styleguide`. The styleguid
|
|||
| Wells | `#wells` | Content wells and containers |
|
||||
| Alerts | `#alerts` | Success, info, warning, danger alerts |
|
||||
| Status | `#status` | Status indicators |
|
||||
| Product Cards | `#cards` | Product grid cards |
|
||||
| Product Cards | `#cards` | Product grid cards, profile card (`.profile-card-header` / `.profile-avatar` / `.profile-handle` / `.profile-email-reveal`), action button grid (`.action-columns` / `.action-button-grid`) |
|
||||
| Cart | `#cart` | Cart and checkout components |
|
||||
| Gift Cards | `#gift-cards` | Gift card purchase, balance check, management |
|
||||
| Comments | `#comments` | Comment form and list |
|
||||
|
|
|
|||
|
|
@ -111,11 +111,34 @@ POST /o/{offer_id}/accept accept current amount (terminal)
|
|||
POST /o/{offer_id}/decline decline current amount (terminal)
|
||||
POST /o/{offer_id}/withdraw buyer-only terminal pull
|
||||
POST /o/{offer_id}/checkout buyer pays accepted offer
|
||||
GET /s/{shop_id}/offers operator inbox of all offers for the shop
|
||||
```
|
||||
|
||||
`offer_open` is registered before the `product_slug` catch-all so
|
||||
`/p/{id}/offer` is not shadowed.
|
||||
|
||||
### Operator inbox (`/s/{shop_id}/offers`)
|
||||
|
||||
`views/offer.py:shop_offers` (`@shop_editor_required`) lists every offer
|
||||
for the shop — open (pending/countered) first, sorted by last action, then
|
||||
terminal offers — in `shop_offers.j2`. Each row links to `/o/{id}` and to
|
||||
the buyer's profile (`/profile/{handle}?shop={shop_id}`). Reachable from
|
||||
`/actions/view` via the "🤝 Offers" button (shown when `shop.offer_enabled`).
|
||||
Incoming offers still email the shop owners (`send_offer_received_email`);
|
||||
this inbox is the in-app counterpart.
|
||||
|
||||
### Identity / privacy
|
||||
|
||||
Offer history and the offer page show the buyer's **display name**
|
||||
(`User.display_name`, which is the public `name` handle — `full_name` is
|
||||
private) linked to `/profile/{handle}`, never the email. The profile page
|
||||
reveals the email only to the user themselves, or to a shop owner/editor
|
||||
viewing in that shop's context (`?shop={shop_id}`) when the profile user
|
||||
has actually transacted there (an offer or an invoice) — see
|
||||
`views/user.py:user_profile`. `_serialize_offer` carries `buyer_name` /
|
||||
`buyer_handle` and per-event `actor_name` / `actor_handle` / `actor_id`
|
||||
(no email).
|
||||
|
||||
### Capability-driven presentation
|
||||
|
||||
Every POST route works as a plain browser form submit: the server flashes
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue