nordabiz/blueprints/admin/routes_benefits.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

218 lines
7.6 KiB
Python

"""
Admin Benefits Routes
=====================
Zarządzanie korzyściami członkowskimi (oferty afiliacyjne).
"""
from datetime import datetime
from flask import render_template, redirect, url_for, flash, request
from flask_login import login_required
from . import bp
from database import SessionLocal, Benefit, BenefitClick, SystemRole
from utils.decorators import role_required
@bp.route('/benefits')
@login_required
@role_required(SystemRole.ADMIN)
def admin_benefits():
"""Lista korzyści w panelu admina."""
db = SessionLocal()
try:
benefits = db.query(Benefit).order_by(
Benefit.is_active.desc(),
Benefit.display_order,
Benefit.name
).all()
# Statystyki
total_clicks = sum(b.click_count or 0 for b in benefits)
active_count = sum(1 for b in benefits if b.is_active)
return render_template(
'admin/benefits_list.html',
benefits=benefits,
total_clicks=total_clicks,
active_count=active_count
)
finally:
db.close()
@bp.route('/benefits/new', methods=['GET', 'POST'])
@login_required
@role_required(SystemRole.ADMIN)
def admin_benefits_new():
"""Dodaj nową korzyść."""
if request.method == 'POST':
db = SessionLocal()
try:
benefit = Benefit(
name=request.form.get('name'),
slug=request.form.get('slug'),
short_description=request.form.get('short_description'),
description=request.form.get('description'),
category=request.form.get('category'),
regular_price=request.form.get('regular_price'),
member_price=request.form.get('member_price'),
discount_description=request.form.get('discount_description'),
affiliate_url=request.form.get('affiliate_url'),
product_url=request.form.get('product_url'),
logo_url=request.form.get('logo_url'),
promo_code=request.form.get('promo_code'),
promo_code_instructions=request.form.get('promo_code_instructions'),
commission_rate=request.form.get('commission_rate'),
commission_duration=request.form.get('commission_duration'),
partner_platform=request.form.get('partner_platform'),
is_featured=request.form.get('is_featured') == 'on',
is_active=request.form.get('is_active') == 'on',
display_order=int(request.form.get('display_order', 0))
)
# Partner since date
partner_since_str = request.form.get('partner_since')
if partner_since_str:
try:
benefit.partner_since = datetime.strptime(partner_since_str, '%Y-%m-%d').date()
except ValueError:
pass
db.add(benefit)
db.commit()
flash(f'Dodano korzyść: {benefit.name}', 'success')
return redirect(url_for('admin.admin_benefits'))
except Exception as e:
db.rollback()
flash(f'Błąd: {str(e)}', 'error')
finally:
db.close()
return render_template(
'admin/benefits_form.html',
benefit=None,
categories=Benefit.CATEGORY_CHOICES
)
@bp.route('/benefits/<int:benefit_id>/edit', methods=['GET', 'POST'])
@login_required
@role_required(SystemRole.ADMIN)
def admin_benefits_edit(benefit_id):
"""Edytuj korzyść."""
db = SessionLocal()
try:
benefit = db.query(Benefit).filter(Benefit.id == benefit_id).first()
if not benefit:
flash('Korzyść nie istnieje.', 'error')
return redirect(url_for('admin.admin_benefits'))
if request.method == 'POST':
benefit.name = request.form.get('name')
benefit.slug = request.form.get('slug')
benefit.short_description = request.form.get('short_description')
benefit.description = request.form.get('description')
benefit.category = request.form.get('category')
benefit.regular_price = request.form.get('regular_price')
benefit.member_price = request.form.get('member_price')
benefit.discount_description = request.form.get('discount_description')
benefit.affiliate_url = request.form.get('affiliate_url')
benefit.product_url = request.form.get('product_url')
benefit.logo_url = request.form.get('logo_url')
benefit.promo_code = request.form.get('promo_code')
benefit.promo_code_instructions = request.form.get('promo_code_instructions')
benefit.commission_rate = request.form.get('commission_rate')
benefit.commission_duration = request.form.get('commission_duration')
benefit.partner_platform = request.form.get('partner_platform')
benefit.is_featured = request.form.get('is_featured') == 'on'
benefit.is_active = request.form.get('is_active') == 'on'
benefit.display_order = int(request.form.get('display_order', 0))
# Partner since date
partner_since_str = request.form.get('partner_since')
if partner_since_str:
try:
benefit.partner_since = datetime.strptime(partner_since_str, '%Y-%m-%d').date()
except ValueError:
pass
db.commit()
flash(f'Zapisano zmiany: {benefit.name}', 'success')
return redirect(url_for('admin.admin_benefits'))
return render_template(
'admin/benefits_form.html',
benefit=benefit,
categories=Benefit.CATEGORY_CHOICES
)
finally:
db.close()
@bp.route('/benefits/<int:benefit_id>/toggle', methods=['POST'])
@login_required
@role_required(SystemRole.ADMIN)
def admin_benefits_toggle(benefit_id):
"""Włącz/wyłącz korzyść."""
db = SessionLocal()
try:
benefit = db.query(Benefit).filter(Benefit.id == benefit_id).first()
if benefit:
benefit.is_active = not benefit.is_active
db.commit()
status = 'włączona' if benefit.is_active else 'wyłączona'
flash(f'Korzyść {benefit.name} została {status}.', 'success')
finally:
db.close()
return redirect(url_for('admin.admin_benefits'))
@bp.route('/benefits/<int:benefit_id>/delete', methods=['POST'])
@login_required
@role_required(SystemRole.ADMIN)
def admin_benefits_delete(benefit_id):
"""Usuń korzyść."""
db = SessionLocal()
try:
benefit = db.query(Benefit).filter(Benefit.id == benefit_id).first()
if benefit:
name = benefit.name
db.delete(benefit)
db.commit()
flash(f'Usunięto korzyść: {name}', 'success')
finally:
db.close()
return redirect(url_for('admin.admin_benefits'))
@bp.route('/benefits/<int:benefit_id>/clicks')
@login_required
@role_required(SystemRole.ADMIN)
def admin_benefits_clicks(benefit_id):
"""Historia kliknięć dla korzyści."""
db = SessionLocal()
try:
benefit = db.query(Benefit).filter(Benefit.id == benefit_id).first()
if not benefit:
flash('Korzyść nie istnieje.', 'error')
return redirect(url_for('admin.admin_benefits'))
clicks = db.query(BenefitClick).filter(
BenefitClick.benefit_id == benefit_id
).order_by(
BenefitClick.clicked_at.desc()
).limit(100).all()
return render_template(
'admin/benefits_clicks.html',
benefit=benefit,
clicks=clicks
)
finally:
db.close()