## Summary - Closes #7 - Portal Retail **Sales** page: 30-day orders/revenue/units/AOV, daily charts, top products - Home dashboard cards for 30-day shop sales and revenue when `FEATURE_SHOP` is on - Stacks on #6 (shop catalog). Merge that first, then retarget `master` if needed. ## Test plan - [ ] Sales page requires login; empty state with no paid orders - [ ] Paid/fulfilled in the last 30 days counted; draft/open/cancelled/old excluded - [ ] Top products ranked by units; home dashboard shows 30-day sales count - [ ] `manage.py test shop core.tests_features` Reviewed-on: #8
web_django_template
Gitea template for AI ML Operations client websites (Web Design & Hosting).
Clone this repo (or Use this template), run scripts/bootstrap-client.sh,
set branding + FEATURE_* flags in the secret env file, deploy.
Always included: public site (landing, about, contact) and client portal
(login, dashboard, leads, UTM analytics). Optional catalog add-ons are
separate Django apps. A flag that is off keeps the app out of
INSTALLED_APPS — no models, admin, URLs, or portal nav.
Catalog → flags
| Catalog item | Flag | App |
|---|---|---|
| Public site | (always on) | public |
| Client portal + UTM | (always on) | accounts, dashboard, leads, analytics, contacts, core |
| Email & SMS | FEATURE_EMAIL_SMS |
email_sms |
| Direct mail (PCM) | FEATURE_DIRECT_MAIL |
directmail |
| Blog | FEATURE_BLOG |
blog |
| Payments (Stripe) | FEATURE_PAYMENTS |
payments (requires email/SMS) |
| Social consolidation | FEATURE_SOCIAL |
social |
| AI social generator | FEATURE_SOCIAL_AI |
social_ai (requires social) |
| E-commerce storefront | FEATURE_SHOP |
shop (requires email/SMS + payments) |
| POS API sync | FEATURE_POS_SYNC |
pos_sync (requires shop) |
| Event ticketing | FEATURE_EVENTS |
events (requires email/SMS + payments) |
| Shipping automation | FEATURE_SHIPPING |
shipping (requires shop) |
New client
# 1. Create repo from this Gitea template, clone it.
# 2. Rename package + branding placeholders:
./scripts/bootstrap-client.sh acme_site "Acme HVAC" acmehvac.com
# 3. Brand public templates and static/brand/ (logo, colors, copy).
# 4. Secrets on the control node:
mkdir -p ~/Documents/secrets/acme_site
cp .env.prod.example ~/Documents/secrets/acme_site/acme_site_prod.env
# Flip only purchased FEATURE_* flags. Fill provider secrets.
./scripts/validate-env.sh ~/Documents/secrets/acme_site/acme_site_prod.env
# 5. Infra: Postgres on 10.0.0.230, server-infra app_catalog, NPM, CI.
# 6. Deploy. Create a portal user (createsuperuser).
Local:
cp .env.example .env
uv sync
cd site
uv run python manage.py migrate
uv run python manage.py runserver
# or: docker compose up
Stack
Django 6 + uv · Postgres (prod/beta); SQLite locally · dj-queue · Docker · server-infra Ansible deploy.
Ollama for social drafts lives on the LAN at http://10.0.0.128:11434.
Do not call a public Ollama host.
Campaign tracked links (piha.li)
When FEATURE_EMAIL_SMS or FEATURE_DIRECT_MAIL is on, composers auto-insert a
homepage URL with utm_source (slug of SITE_NAME, or UTM_SOURCE),
utm_medium = channel, and utm_campaign = campaign name. Configured
SHORTENER_BASE_URL + SHORTENER_API_TOKEN mint that URL via
url_shortening_service
and put piha.li / beta.piha.li short links in SMS, email hrefs, and postcard QR codes.
Caller secrets (this repo's env file):
SHORTENER_BASE_URL=https://shortener.aimloperations.com
SHORTENER_API_TOKEN=<slug>:<secret>
Operator secrets (url_shortening_service_*): SHORTENER_API_TOKENS=<slug>:<same-secret>
and SHORT_ALLOWED_HOSTS must include the client domain. Mint against the API host,
never piha.li.