feat: add Loyalty+ platform with pages (Phase 6)

- Add alembic migration for Loyalty platform (z5f6g7h8i9j0)
- Insert platform: code='loyalty', domain='loyalty.lu', path_prefix='loyalty'
- Create platform marketing pages: home, pricing, features, how-it-works
- Create vendor default pages: about, rewards-catalog, terms, privacy
- Update implementation plan to mark Phase 6 complete

Platform routing handled automatically by PlatformContextMiddleware
via path_prefix detection for /loyalty/* URLs.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-01-19 16:46:15 +01:00
parent 25653b93e1
commit 32bcbc881d
2 changed files with 450 additions and 18 deletions

View File

@@ -0,0 +1,424 @@
"""add loyalty platform
Revision ID: z5f6g7h8i9j0
Revises: z4e5f6a7b8c9
Create Date: 2026-01-19 12:00:00.000000
This migration adds the Loyalty+ platform:
1. Inserts loyalty platform record
2. Creates platform marketing pages (home, pricing, features, how-it-works)
3. Creates vendor default pages (about, rewards-catalog, terms, privacy)
"""
from typing import Sequence, Union
import sqlalchemy as sa
from alembic import op
# revision identifiers, used by Alembic.
revision: str = "z5f6g7h8i9j0"
down_revision: Union[str, None] = "z4e5f6a7b8c9"
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
conn = op.get_bind()
# =========================================================================
# 1. Insert Loyalty platform
# =========================================================================
conn.execute(
sa.text("""
INSERT INTO platforms (code, name, description, domain, path_prefix, default_language,
supported_languages, is_active, is_public, theme_config, settings,
created_at, updated_at)
VALUES ('loyalty', 'Loyalty+', 'Customer loyalty program platform for Luxembourg businesses',
'loyalty.lu', 'loyalty', 'fr', '["fr", "de", "en"]', true, true,
'{"primary_color": "#8B5CF6", "secondary_color": "#A78BFA"}',
'{"features": ["points", "rewards", "tiers", "analytics"]}',
CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)
""")
)
# Get the Loyalty platform ID
result = conn.execute(sa.text("SELECT id FROM platforms WHERE code = 'loyalty'"))
loyalty_platform_id = result.fetchone()[0]
# =========================================================================
# 2. Create platform marketing pages (is_platform_page=True)
# =========================================================================
platform_pages = [
{
"slug": "home",
"title": "Loyalty+ - Customer Loyalty Platform",
"content": """<div class="hero-section">
<h1>Build Customer Loyalty That Lasts</h1>
<p class="lead">Reward your customers, increase retention, and grow your business with Loyalty+</p>
</div>
<div class="features-grid">
<div class="feature">
<h3>Points & Rewards</h3>
<p>Create custom point systems that incentivize repeat purchases and customer engagement.</p>
</div>
<div class="feature">
<h3>Member Tiers</h3>
<p>Reward your best customers with exclusive benefits and VIP treatment.</p>
</div>
<div class="feature">
<h3>Real-time Analytics</h3>
<p>Track program performance and customer behavior with detailed insights.</p>
</div>
</div>""",
"meta_description": "Loyalty+ is Luxembourg's leading customer loyalty platform. Build lasting relationships with your customers through points, rewards, and personalized experiences.",
"show_in_header": False,
"show_in_footer": False,
"display_order": 0,
},
{
"slug": "pricing",
"title": "Pricing - Loyalty+",
"content": """<div class="pricing-header">
<h1>Simple, Transparent Pricing</h1>
<p>Choose the plan that fits your business</p>
</div>
<div class="pricing-grid">
<div class="pricing-card">
<h3>Starter</h3>
<div class="price">€49<span>/month</span></div>
<ul>
<li>Up to 500 members</li>
<li>Basic point system</li>
<li>Email support</li>
<li>Standard rewards</li>
</ul>
</div>
<div class="pricing-card featured">
<h3>Growth</h3>
<div class="price">€149<span>/month</span></div>
<ul>
<li>Up to 5,000 members</li>
<li>Advanced point rules</li>
<li>Priority support</li>
<li>Custom rewards</li>
<li>Member tiers</li>
<li>Analytics dashboard</li>
</ul>
</div>
<div class="pricing-card">
<h3>Enterprise</h3>
<div class="price">Custom</div>
<ul>
<li>Unlimited members</li>
<li>Full API access</li>
<li>Dedicated support</li>
<li>Custom integrations</li>
<li>White-label options</li>
</ul>
</div>
</div>""",
"meta_description": "Loyalty+ pricing plans starting at €49/month. Choose Starter, Growth, or Enterprise for your customer loyalty program.",
"show_in_header": True,
"show_in_footer": True,
"display_order": 1,
},
{
"slug": "features",
"title": "Features - Loyalty+",
"content": """<div class="features-header">
<h1>Powerful Features for Modern Loyalty</h1>
<p>Everything you need to build and manage a successful loyalty program</p>
</div>
<div class="feature-section">
<h2>Points & Earning Rules</h2>
<p>Create flexible point systems with custom earning rules based on purchases, actions, or special events.</p>
<ul>
<li>Points per euro spent</li>
<li>Bonus point campaigns</li>
<li>Birthday & anniversary rewards</li>
<li>Referral bonuses</li>
</ul>
</div>
<div class="feature-section">
<h2>Rewards Catalog</h2>
<p>Offer enticing rewards that keep customers coming back.</p>
<ul>
<li>Discount vouchers</li>
<li>Free products</li>
<li>Exclusive experiences</li>
<li>Partner rewards</li>
</ul>
</div>
<div class="feature-section">
<h2>Member Tiers</h2>
<p>Recognize and reward your most loyal customers with tiered benefits.</p>
<ul>
<li>Bronze, Silver, Gold, Platinum levels</li>
<li>Automatic tier progression</li>
<li>Exclusive tier benefits</li>
<li>VIP experiences</li>
</ul>
</div>
<div class="feature-section">
<h2>Analytics & Insights</h2>
<p>Make data-driven decisions with comprehensive analytics.</p>
<ul>
<li>Member activity tracking</li>
<li>Redemption analytics</li>
<li>ROI calculations</li>
<li>Custom reports</li>
</ul>
</div>""",
"meta_description": "Explore Loyalty+ features: points systems, rewards catalog, member tiers, and analytics. Build the perfect loyalty program for your business.",
"show_in_header": True,
"show_in_footer": True,
"display_order": 2,
},
{
"slug": "how-it-works",
"title": "How It Works - Loyalty+",
"content": """<div class="how-header">
<h1>Getting Started is Easy</h1>
<p>Launch your loyalty program in just a few steps</p>
</div>
<div class="steps">
<div class="step">
<div class="step-number">1</div>
<h3>Sign Up</h3>
<p>Create your account and choose your plan. No credit card required for the free trial.</p>
</div>
<div class="step">
<div class="step-number">2</div>
<h3>Configure Your Program</h3>
<p>Set up your point rules, rewards, and member tiers using our intuitive dashboard.</p>
</div>
<div class="step">
<div class="step-number">3</div>
<h3>Integrate</h3>
<p>Connect Loyalty+ to your POS, e-commerce, or app using our APIs and plugins.</p>
</div>
<div class="step">
<div class="step-number">4</div>
<h3>Launch & Grow</h3>
<p>Invite your customers and watch your loyalty program drive results.</p>
</div>
</div>
<div class="cta-section">
<h2>Ready to Build Customer Loyalty?</h2>
<p>Start your free 14-day trial today.</p>
<a href="/loyalty/signup" class="btn-primary">Get Started Free</a>
</div>""",
"meta_description": "Learn how to launch your Loyalty+ program in 4 easy steps. Sign up, configure, integrate, and start building customer loyalty today.",
"show_in_header": True,
"show_in_footer": True,
"display_order": 3,
},
]
for page in platform_pages:
conn.execute(
sa.text("""
INSERT INTO content_pages (platform_id, vendor_id, slug, title, content, content_format,
meta_description, is_published, is_platform_page,
show_in_header, show_in_footer, display_order,
created_at, updated_at)
VALUES (:platform_id, NULL, :slug, :title, :content, 'html',
:meta_description, true, true,
:show_in_header, :show_in_footer, :display_order,
CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)
"""),
{
"platform_id": loyalty_platform_id,
"slug": page["slug"],
"title": page["title"],
"content": page["content"],
"meta_description": page["meta_description"],
"show_in_header": page["show_in_header"],
"show_in_footer": page["show_in_footer"],
"display_order": page["display_order"],
}
)
# =========================================================================
# 3. Create vendor default pages (is_platform_page=False)
# =========================================================================
vendor_defaults = [
{
"slug": "about",
"title": "About Us",
"content": """<div class="about-page">
<h1>About Our Loyalty Program</h1>
<p>Welcome to our customer loyalty program! We value your continued support and want to reward you for being part of our community.</p>
<h2>Why Join?</h2>
<ul>
<li><strong>Earn Points:</strong> Get points on every purchase</li>
<li><strong>Exclusive Rewards:</strong> Redeem points for discounts and special offers</li>
<li><strong>Member Benefits:</strong> Access exclusive deals and early sales</li>
<li><strong>Birthday Surprises:</strong> Special rewards on your birthday</li>
</ul>
<h2>How It Works</h2>
<p>Simply sign up, start earning points with every purchase, and redeem them for rewards you'll love.</p>
</div>""",
"meta_description": "Learn about our customer loyalty program. Earn points, unlock rewards, and enjoy exclusive member benefits.",
"show_in_header": False,
"show_in_footer": True,
"display_order": 10,
},
{
"slug": "rewards-catalog",
"title": "Rewards Catalog",
"content": """<div class="rewards-page">
<h1>Rewards Catalog</h1>
<p>Browse our selection of rewards and redeem your hard-earned points!</p>
<div class="rewards-grid">
<div class="reward-placeholder">
<p>Your rewards catalog will appear here once configured.</p>
</div>
</div>
<h2>How to Redeem</h2>
<ol>
<li>Check your point balance in your account</li>
<li>Browse available rewards</li>
<li>Click "Redeem" on your chosen reward</li>
<li>Use your reward code at checkout</li>
</ol>
</div>""",
"meta_description": "Browse and redeem your loyalty points for exclusive rewards, discounts, and special offers.",
"show_in_header": True,
"show_in_footer": True,
"display_order": 11,
},
{
"slug": "terms",
"title": "Loyalty Program Terms & Conditions",
"content": """<div class="terms-page">
<h1>Loyalty Program Terms & Conditions</h1>
<p class="last-updated">Last updated: January 2026</p>
<h2>1. Program Membership</h2>
<p>Membership in our loyalty program is free and open to all customers who meet the eligibility requirements.</p>
<h2>2. Earning Points</h2>
<p>Points are earned on qualifying purchases. The earning rate and qualifying purchases are determined by the program operator and may change with notice.</p>
<h2>3. Redeeming Points</h2>
<p>Points can be redeemed for rewards as shown in the rewards catalog. Minimum point thresholds may apply.</p>
<h2>4. Point Expiration</h2>
<p>Points may expire after a period of account inactivity. Members will be notified before points expire.</p>
<h2>5. Program Changes</h2>
<p>We reserve the right to modify, suspend, or terminate the program with reasonable notice to members.</p>
<h2>6. Privacy</h2>
<p>Your personal information is handled in accordance with our Privacy Policy.</p>
</div>""",
"meta_description": "Read the terms and conditions for our customer loyalty program including earning rules, redemption, and point expiration policies.",
"show_in_header": False,
"show_in_footer": True,
"show_in_legal": True,
"display_order": 20,
},
{
"slug": "privacy",
"title": "Privacy Policy",
"content": """<div class="privacy-page">
<h1>Privacy Policy</h1>
<p class="last-updated">Last updated: January 2026</p>
<h2>Information We Collect</h2>
<p>We collect information you provide when joining our loyalty program, including:</p>
<ul>
<li>Name and contact information</li>
<li>Purchase history and preferences</li>
<li>Point balance and redemption history</li>
</ul>
<h2>How We Use Your Information</h2>
<p>Your information helps us:</p>
<ul>
<li>Manage your loyalty account</li>
<li>Process point earnings and redemptions</li>
<li>Send program updates and personalized offers</li>
<li>Improve our services</li>
</ul>
<h2>Data Protection</h2>
<p>We implement appropriate security measures to protect your personal information in accordance with GDPR and Luxembourg data protection laws.</p>
<h2>Your Rights</h2>
<p>You have the right to access, correct, or delete your personal data. Contact us to exercise these rights.</p>
<h2>Contact</h2>
<p>For privacy inquiries, please contact our data protection officer.</p>
</div>""",
"meta_description": "Our privacy policy explains how we collect, use, and protect your personal information in our loyalty program.",
"show_in_header": False,
"show_in_footer": True,
"show_in_legal": True,
"display_order": 21,
},
]
for page in vendor_defaults:
show_in_legal = page.get("show_in_legal", False)
conn.execute(
sa.text("""
INSERT INTO content_pages (platform_id, vendor_id, slug, title, content, content_format,
meta_description, is_published, is_platform_page,
show_in_header, show_in_footer, show_in_legal, display_order,
created_at, updated_at)
VALUES (:platform_id, NULL, :slug, :title, :content, 'html',
:meta_description, true, false,
:show_in_header, :show_in_footer, :show_in_legal, :display_order,
CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)
"""),
{
"platform_id": loyalty_platform_id,
"slug": page["slug"],
"title": page["title"],
"content": page["content"],
"meta_description": page["meta_description"],
"show_in_header": page["show_in_header"],
"show_in_footer": page["show_in_footer"],
"show_in_legal": show_in_legal,
"display_order": page["display_order"],
}
)
def downgrade() -> None:
conn = op.get_bind()
# Get the Loyalty platform ID
result = conn.execute(sa.text("SELECT id FROM platforms WHERE code = 'loyalty'"))
row = result.fetchone()
if row:
loyalty_platform_id = row[0]
# Delete all content pages for loyalty platform
conn.execute(
sa.text("DELETE FROM content_pages WHERE platform_id = :platform_id"),
{"platform_id": loyalty_platform_id}
)
# Delete vendor_platforms entries for loyalty
conn.execute(
sa.text("DELETE FROM vendor_platforms WHERE platform_id = :platform_id"),
{"platform_id": loyalty_platform_id}
)
# Delete loyalty platform
conn.execute(sa.text("DELETE FROM platforms WHERE code = 'loyalty'"))

View File

@@ -1,10 +1,11 @@
# Multi-Platform CMS Architecture - Implementation Plan
> **Status:** Phases 1-5 Complete | Phase 6 Pending
> **Status:** All Phases Complete (1-6)
> **Last Updated:** 2026-01-19
> **Commits:**
> - `408019d` (Phase 1: Database & Models)
> - `9680026` (Phases 2-5: Migration, Admin, Docs, Vendor Dashboard)
> - Phase 6: Loyalty Platform Setup (pending commit)
---
@@ -193,29 +194,36 @@ Development:
---
## Phase 6: Loyalty Platform Setup
## Phase 6: Loyalty Platform Setup ✅ COMPLETE
### 6.1 Database Setup
### 6.1 Database Setup
```sql
-- Insert loyalty platform
INSERT INTO platforms (code, name, description, domain, path_prefix, ...)
VALUES ('loyalty', 'Loyalty+', 'Customer loyalty program', 'loyalty.lu', 'loyalty', ...);
```
Migration: `alembic/versions/z5f6g7h8i9j0_add_loyalty_platform.py`
### 6.2 Create Platform Pages
Inserts Loyalty platform with:
- code: `loyalty`
- name: `Loyalty+`
- domain: `loyalty.lu`
- path_prefix: `loyalty`
- theme_config: purple color scheme
- [ ] Loyalty homepage
- [ ] Loyalty pricing
- [ ] Loyalty features
- [ ] How it works
### 6.2 Platform Marketing Pages ✅
### 6.3 Create Vendor Defaults
| Page | Slug | Status |
|------|------|--------|
| Homepage | `home` | ✅ |
| Pricing | `pricing` | ✅ |
| Features | `features` | ✅ |
| How It Works | `how-it-works` | ✅ |
- [ ] About (loyalty-specific)
- [ ] Rewards catalog
- [ ] Terms of service
- [ ] Privacy policy
### 6.3 Vendor Default Pages ✅
| Page | Slug | Status |
|------|------|--------|
| About | `about` | ✅ |
| Rewards Catalog | `rewards-catalog` | ✅ |
| Terms | `terms` | ✅ |
| Privacy | `privacy` | ✅ |
---