nordabiz/blueprints/benefits/routes.py
Maciej Pienczyn 5fd5140763
Some checks are pending
NordaBiz Tests / Unit & Integration Tests (push) Waiting to run
NordaBiz Tests / E2E Tests (Playwright) (push) Blocked by required conditions
NordaBiz Tests / Smoke Tests (Production) (push) Blocked by required conditions
NordaBiz Tests / Send Failure Notification (push) Blocked by required conditions
feat: Add member benefits module with WisprFlow affiliate
- Add Benefit and BenefitClick models for tracking affiliate offers
- Create /korzysci blueprint with admin-only access (test mode)
- Add admin panel at /admin/benefits for managing offers
- Include WisprFlow as first benefit with branded link ref.wisprflow.ai/norda
- Add QR code support for printed materials
- Track clicks with user attribution and analytics

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-02 22:26:44 +01:00

111 lines
3.0 KiB
Python

"""
Benefits Routes
===============
Korzyści członkowskie - oferty afiliacyjne dla członków Izby NORDA.
TRYB TESTOWY: Dostęp tylko dla administratorów.
Po uruchomieniu produkcyjnym zmienić na @member_required.
"""
from flask import render_template, redirect, request, flash, url_for, abort
from flask_login import login_required, current_user
from . import bp
from database import SessionLocal, Benefit, BenefitClick, SystemRole
from utils.decorators import role_required
@bp.route('/')
@login_required
@role_required(SystemRole.ADMIN) # TESTOWY: tylko admin
def benefits_list():
"""Lista korzyści członkowskich."""
db = SessionLocal()
try:
benefits = db.query(Benefit).filter(
Benefit.is_active == True
).order_by(
Benefit.is_featured.desc(),
Benefit.display_order,
Benefit.name
).all()
# Grupuj po kategorii
categories = {}
for benefit in benefits:
cat = benefit.category or 'other'
if cat not in categories:
categories[cat] = []
categories[cat].append(benefit)
return render_template(
'benefits/benefits_list.html',
benefits=benefits,
categories=categories,
category_choices=dict(Benefit.CATEGORY_CHOICES)
)
finally:
db.close()
@bp.route('/<slug>')
@login_required
@role_required(SystemRole.ADMIN) # TESTOWY: tylko admin
def benefit_detail(slug):
"""Szczegóły korzyści i przekierowanie na link afiliacyjny."""
db = SessionLocal()
try:
benefit = db.query(Benefit).filter(
Benefit.slug == slug,
Benefit.is_active == True
).first()
if not benefit:
abort(404)
return render_template(
'benefits/benefit_detail.html',
benefit=benefit
)
finally:
db.close()
@bp.route('/<slug>/go')
@login_required
@role_required(SystemRole.ADMIN) # TESTOWY: tylko admin
def benefit_redirect(slug):
"""Przekierowanie na link afiliacyjny z logowaniem kliknięcia."""
db = SessionLocal()
try:
benefit = db.query(Benefit).filter(
Benefit.slug == slug,
Benefit.is_active == True
).first()
if not benefit:
abort(404)
if not benefit.affiliate_url:
flash('Link do tej oferty nie jest jeszcze dostępny.', 'warning')
return redirect(url_for('benefits.benefits_list'))
# Loguj kliknięcie
click = BenefitClick(
benefit_id=benefit.id,
user_id=current_user.id if current_user.is_authenticated else None,
ip_address=request.remote_addr,
user_agent=request.headers.get('User-Agent', '')[:500]
)
db.add(click)
# Zwiększ licznik
benefit.click_count = (benefit.click_count or 0) + 1
db.commit()
return redirect(benefit.affiliate_url)
finally:
db.close()