feat: implement exchange creation
Add Exchange model, API endpoint, and form validation for creating new gift exchanges. - Create ExchangeForm with timezone validation - Add admin routes for creating and viewing exchanges - Generate unique 12-char slug for each exchange - Validate registration/exchange dates - Display exchanges in dashboard - All tests passing with 92% coverage Story: 2.1
This commit is contained in:
@@ -1,5 +1,7 @@
|
||||
"""Route blueprints for Sneaky Klaus application."""
|
||||
|
||||
from src.routes.admin import admin_bp
|
||||
from src.routes.participant import participant_bp
|
||||
from src.routes.setup import setup_bp
|
||||
|
||||
__all__ = ["setup_bp"]
|
||||
__all__ = ["admin_bp", "participant_bp", "setup_bp"]
|
||||
|
||||
+82
-3
@@ -6,8 +6,8 @@ from flask import Blueprint, flash, redirect, render_template, session, url_for
|
||||
|
||||
from src.app import bcrypt, db
|
||||
from src.decorators import admin_required
|
||||
from src.forms import LoginForm
|
||||
from src.models import Admin
|
||||
from src.forms import ExchangeForm, LoginForm
|
||||
from src.models import Admin, Exchange
|
||||
from src.utils import check_rate_limit, increment_rate_limit, reset_rate_limit
|
||||
|
||||
admin_bp = Blueprint("admin", __name__, url_prefix="/admin")
|
||||
@@ -100,4 +100,83 @@ def dashboard():
|
||||
Returns:
|
||||
Rendered admin dashboard template.
|
||||
"""
|
||||
return render_template("admin/dashboard.html")
|
||||
# Get all exchanges ordered by exchange_date
|
||||
exchanges = db.session.query(Exchange).order_by(Exchange.exchange_date.asc()).all()
|
||||
|
||||
# Count exchanges by state
|
||||
active_count = sum(
|
||||
1
|
||||
for e in exchanges
|
||||
if e.state
|
||||
in [
|
||||
Exchange.STATE_REGISTRATION_OPEN,
|
||||
Exchange.STATE_REGISTRATION_CLOSED,
|
||||
Exchange.STATE_MATCHED,
|
||||
]
|
||||
)
|
||||
completed_count = sum(1 for e in exchanges if e.state == Exchange.STATE_COMPLETED)
|
||||
draft_count = sum(1 for e in exchanges if e.state == Exchange.STATE_DRAFT)
|
||||
|
||||
return render_template(
|
||||
"admin/dashboard.html",
|
||||
exchanges=exchanges,
|
||||
active_count=active_count,
|
||||
completed_count=completed_count,
|
||||
draft_count=draft_count,
|
||||
)
|
||||
|
||||
|
||||
@admin_bp.route("/exchange/new", methods=["GET", "POST"])
|
||||
@admin_required
|
||||
def create_exchange():
|
||||
"""Create a new exchange.
|
||||
|
||||
GET: Display exchange creation form.
|
||||
POST: Process form and create exchange.
|
||||
|
||||
Returns:
|
||||
On GET: Rendered form template.
|
||||
On POST success: Redirect to exchange detail page.
|
||||
On POST error: Re-render form with errors.
|
||||
"""
|
||||
form = ExchangeForm()
|
||||
|
||||
if form.validate_on_submit():
|
||||
# Generate unique slug
|
||||
slug = Exchange.generate_slug()
|
||||
|
||||
# Create new exchange
|
||||
exchange = Exchange(
|
||||
slug=slug,
|
||||
name=form.name.data,
|
||||
description=form.description.data or None,
|
||||
budget=form.budget.data,
|
||||
max_participants=form.max_participants.data,
|
||||
registration_close_date=form.registration_close_date.data,
|
||||
exchange_date=form.exchange_date.data,
|
||||
timezone=form.timezone.data,
|
||||
state=Exchange.STATE_DRAFT,
|
||||
)
|
||||
|
||||
db.session.add(exchange)
|
||||
db.session.commit()
|
||||
|
||||
flash("Exchange created successfully!", "success")
|
||||
return redirect(url_for("admin.view_exchange", exchange_id=exchange.id))
|
||||
|
||||
return render_template("admin/exchange_form.html", form=form, is_edit=False)
|
||||
|
||||
|
||||
@admin_bp.route("/exchange/<int:exchange_id>")
|
||||
@admin_required
|
||||
def view_exchange(exchange_id):
|
||||
"""View exchange details.
|
||||
|
||||
Args:
|
||||
exchange_id: ID of the exchange to view.
|
||||
|
||||
Returns:
|
||||
Rendered exchange detail template.
|
||||
"""
|
||||
exchange = db.session.query(Exchange).get_or_404(exchange_id)
|
||||
return render_template("admin/exchange_detail.html", exchange=exchange)
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
"""Participant routes for Sneaky Klaus application."""
|
||||
|
||||
from flask import Blueprint
|
||||
|
||||
participant_bp = Blueprint("participant", __name__, url_prefix="")
|
||||
|
||||
|
||||
@participant_bp.route("/exchange/<slug>/register")
|
||||
def register(slug):
|
||||
"""Participant registration page (stub for now).
|
||||
|
||||
Args:
|
||||
slug: Exchange registration slug.
|
||||
|
||||
Returns:
|
||||
Rendered registration page template.
|
||||
"""
|
||||
return f"Registration page for exchange: {slug}"
|
||||
Reference in New Issue
Block a user