Compare commits

..

13 Commits

Author SHA1 Message Date
giles
41b4e0fe24 fix: remove existing bp dir before symlinking in Dockerfile
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 48s
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-11 01:08:09 +00:00
giles
b8f57ef92f fix: register cart loader so g.cart is available for product cart route
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 35s
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-11 00:52:29 +00:00
giles
20fde958ec fix: correct endpoint name to market.browse.product.cart
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 38s
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-11 00:41:00 +00:00
giles
9ee0c91106 fix: use url_for for cart/product URLs to include page/market slugs
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 35s
market_url() generates URLs without the /<page_slug>/<market_slug>
prefix, causing 405 on POST /cart. url_for() auto-injects the slugs
via url_defaults.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-11 00:30:02 +00:00
giles
fd205b9b61 fix: inject post data via blueprint context processor
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 33s
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-11 00:17:13 +00:00
giles
2788e24ec8 fix: inject post data into template context for post header row
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 36s
The post header bar needs `post` in the template context to render
the page link, feature image, and nav. Added context processor to
spread g.post_data into templates, matching the events app pattern.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-11 00:12:35 +00:00
giles
863429d51b fix: add post header bar to market pages and show market name
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 36s
- Add post header row to market/index.html template chain
- Fix OOB templates to include post header on HTMX navigation
- Show market name instead of static coop_title in header
- Restore trailing slash on POST /cart/ route; fix templates to
  include trailing slash in cart URLs

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-10 23:55:16 +00:00
giles
93e1e6d41e fix: remove trailing slash from POST /cart route
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 39s
Templates generate URLs without trailing slash via market_url(), but
the route required one. POST requests don't get redirected by Werkzeug,
causing a 405 MethodNotAllowed.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-10 23:52:25 +00:00
giles
7065acb94d chore: move repo to ~/rose-ash/ and add configurable CI paths
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 47s
REPO_DIR points to /root/rose-ash/market, COOP_DIR to /root/coop.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-10 22:05:42 +00:00
giles
9fb26129b6 chore: update shared_lib submodule to Phase 4
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 1m8s
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-10 21:47:46 +00:00
giles
6a378a2425 fix: use cart_url() instead of url_for('cart.view_cart') for cross-app redirect
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 57s
The market app doesn't have a cart blueprint registered, so url_for()
would fail. Uses the cross-app cart_url() helper instead.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-10 21:45:54 +00:00
giles
6459e2406e chore: update shared_lib submodule to Phase 3
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 33s
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-10 20:54:19 +00:00
giles
ce1847e06c feat: set market_place_id on CartItem when adding to cart (Phase 3)
Track which market a cart item came from by setting market_place_id
from g.market.id on new CartItem creation.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-10 20:49:37 +00:00
10 changed files with 40 additions and 25 deletions

View File

@@ -7,7 +7,8 @@ on:
env:
REGISTRY: registry.rose-ash.com:5000
IMAGE: market
REPO_DIR: /root/market
REPO_DIR: /root/rose-ash/market
COOP_DIR: /root/coop
jobs:
build-and-deploy:
@@ -58,7 +59,7 @@ jobs:
DEPLOY_HOST: ${{ secrets.DEPLOY_HOST }}
run: |
ssh "root@$DEPLOY_HOST" "
cd /root/coop
cd ${{ env.COOP_DIR }}
source .env
docker stack deploy -c docker-compose.yml coop
echo 'Waiting for services to update...'

View File

@@ -23,7 +23,7 @@ RUN pip install -r requirements.txt
COPY . .
# Link app blueprints into the shared library's namespace
RUN ln -s /app/bp /app/shared_lib/suma_browser/app/bp
RUN rm -rf /app/shared_lib/suma_browser/app/bp && ln -s /app/bp /app/shared_lib/suma_browser/app/bp
# ---------- Runtime setup ----------
COPY entrypoint.sh /usr/local/bin/entrypoint.sh

10
app.py
View File

@@ -8,6 +8,7 @@ from jinja2 import FileSystemLoader, ChoiceLoader
from sqlalchemy import select
from shared.factory import create_base_app
from shared.cart_loader import load_cart
from config import config
from suma_browser.app.bp import register_market_bp
@@ -45,7 +46,7 @@ def create_app() -> "Quart":
from models.market_place import MarketPlace
from models.ghost_content import Post
app = create_base_app("market", context_fn=market_context)
app = create_base_app("market", context_fn=market_context, before_request_fns=[load_cart])
# App-specific templates override shared templates
app_templates = str(Path(__file__).resolve().parent / "templates")
@@ -124,6 +125,13 @@ def create_app() -> "Quart":
abort(404)
g.market = market
@app.context_processor
async def inject_post():
post_data = getattr(g, "post_data", None)
if not post_data:
return {}
return {**post_data}
# --- Root route: market listing ---
@app.get("/")
async def markets_listing():

View File

@@ -24,8 +24,10 @@ def register(url_prefix, title):
async def inject_root():
market = getattr(g, "market", None)
market_id = market.id if market else None
post_data = getattr(g, "post_data", None) or {}
return {
"coop_title": title,
**post_data,
"coop_title": market.name if market else title,
"categories": (await get_nav(g.s, market_id=market_id))["cats"],
"qs": makeqs_factory()(),
"market": market,

View File

@@ -221,6 +221,7 @@ def register():
product_id=product.id,
product=product,
quantity=count,
market_place_id=getattr(g, "market", None) and g.market.id,
)
g.cart.append(ci)
g.s.add(ci)
@@ -241,7 +242,8 @@ def register():
)
# normal POST: go to cart page
return redirect(url_for("cart.view_cart"))
from shared.urls import cart_url
return redirect(cart_url("/"))

View File

@@ -11,9 +11,9 @@
{% block oobs %}
{% from '_types/root/_n/macros.html' import oob_header with context %}
{{oob_header('root-header-child', 'market-header-child', '_types/market/header/_header.html')}}
{{oob_header('post-header-child', 'market-header-child', '_types/market/header/_header.html')}}
{% from '_types/root/header/_header.html' import header_row with context %}
{% from '_types/post/header/_header.html' import header_row with context %}
{{ header_row(oob=True) }}
{% endblock %}

View File

@@ -11,9 +11,9 @@
{% block oobs %}
{% from '_types/root/_n/macros.html' import oob_header with context %}
{{oob_header('root-header-child', 'market-header-child', '_types/market/header/_header.html')}}
{{oob_header('post-header-child', 'market-header-child', '_types/market/header/_header.html')}}
{% from '_types/root/header/_header.html' import header_row with context %}
{% from '_types/post/header/_header.html' import header_row with context %}
{{ header_row(oob=True) }}
{% endblock %}

View File

@@ -3,9 +3,11 @@
{% block root_header_child %}
{% from '_types/root/_n/macros.html' import index_row with context %}
{% call index_row('market-header-child', '_types/market/header/_header.html') %}
{% block market_header_child %}
{% endblock %}
{% call index_row('post-header-child', '_types/post/header/_header.html') %}
{% call index_row('market-header-child', '_types/market/header/_header.html') %}
{% block market_header_child %}
{% endblock %}
{% endcall %}
{% endcall %}
{% endblock %}

View File

@@ -7,9 +7,9 @@
{% if not quantity %}
<form
action="{{ market_url('/product/' + slug + '/cart') }}"
action="{{ url_for('market.browse.product.cart', slug=slug) }}"
method="post"
hx-post="{{ market_url('/product/' + slug + '/cart') }}"
hx-post="{{ url_for('market.browse.product.cart', slug=slug) }}"
hx-target="#cart-mini"
hx-swap="outerHTML"
class="rounded flex items-center"
@@ -38,9 +38,9 @@
<div class="rounded flex items-center gap-2">
<!-- minus -->
<form
action="{{ market_url('/product/' + slug + '/cart') }}"
action="{{ url_for('market.browse.product.cart', slug=slug) }}"
method="post"
hx-post="{{ market_url('/product/' + slug + '/cart') }}"
hx-post="{{ url_for('market.browse.product.cart', slug=slug) }}"
hx-target="#cart-mini"
hx-swap="outerHTML"
>
@@ -80,9 +80,9 @@
<!-- plus -->
<form
action="{{ market_url('/product/' + slug + '/cart') }}"
action="{{ url_for('market.browse.product.cart', slug=slug) }}"
method="post"
hx-post="{{ market_url('/product/' + slug + '/cart') }}"
hx-post="{{ url_for('market.browse.product.cart', slug=slug) }}"
hx-target="#cart-mini"
hx-swap="outerHTML"
>
@@ -139,7 +139,7 @@
<div class="flex flex-col sm:flex-row sm:items-start justify-between gap-2 sm:gap-3">
<div class="min-w-0">
<h2 class="text-sm sm:text-base md:text-lg font-semibold text-stone-900">
{% set href=market_url('/product/' + p.slug + '/') %}
{% set href=url_for('market.browse.product.product_detail', slug=p.slug) %}
<a
href="{{ href }}"
hx_get="{{href}}"
@@ -189,9 +189,9 @@
<div class="flex items-center gap-2 text-xs sm:text-sm text-stone-700">
<span class="text-[0.65rem] sm:text-xs uppercase tracking-wide text-stone-500">Quantity</span>
<form
action="{{ market_url('/product/' + p.slug + '/cart') }}"
action="{{ url_for('market.browse.product.cart', slug=p.slug) }}"
method="post"
hx-post="{{ market_url('/product/' + p.slug + '/cart') }}"
hx-post="{{ url_for('market.browse.product.cart', slug=p.slug) }}"
hx-target="#cart-mini"
hx-swap="outerHTML"
>
@@ -212,9 +212,9 @@
{{ item.quantity }}
</span>
<form
action="{{ market_url('/product/' + p.slug + '/cart') }}"
action="{{ url_for('market.browse.product.cart', slug=p.slug) }}"
method="post"
hx-post="{{ market_url('/product/' + p.slug + '/cart') }}"
hx-post="{{ url_for('market.browse.product.cart', slug=p.slug) }}"
hx-target="#cart-mini"
hx-swap="outerHTML"
>