feat: implement Story 4.2 - New Participant Registration

Implement participant registration with the following features:

- POST handler for /exchange/<slug>/register
- Create Participant record with lowercased email
- Generate magic token and send confirmation email
- Redirect to success page after registration
- Rate limiting: 10 registrations per hour per IP
- Validation for exchange state (must be registration_open)
- Form validation for required fields (name, email)
- Email format validation
- Optional fields support (gift_ideas, reminder_enabled)

Also includes:
- Registration success page template
- 429 error handling template
- Flash message support in base template
- Test config update for email service dev mode
- Comprehensive test suite with 8 tests

All tests passing (86 total), 91% coverage maintained.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2025-12-22 17:14:11 -07:00
parent 81e2cb8c86
commit 3467d97828
8 changed files with 506 additions and 6 deletions

View File

@@ -0,0 +1,12 @@
{% extends "layouts/base.html" %}
{% block title %}Too Many Requests - Sneaky Klaus{% endblock %}
{% block content %}
<article>
<header>
<h1>429 - Too Many Requests</h1>
</header>
<p>Too many registration attempts. Please try again later.</p>
</article>
{% endblock %}

View File

@@ -12,6 +12,15 @@
</head>
<body>
<main class="container">
{% with messages = get_flashed_messages(with_categories=true) %}
{% if messages %}
{% for category, message in messages %}
<article role="alert">
<p>{{ message }}</p>
</article>
{% endfor %}
{% endif %}
{% endwith %}
{% block content %}{% endblock %}
</main>

View File

@@ -0,0 +1,33 @@
{% extends "layouts/base.html" %}
{% block title %}Registration Complete - {{ exchange.name }}{% endblock %}
{% block content %}
<article>
<header>
<h1>Registration Complete!</h1>
</header>
<section>
<p>You've successfully registered for <strong>{{ exchange.name }}</strong>.</p>
<p>Check your email for a confirmation message with a magic link to access your participant dashboard.</p>
<h3>What's Next?</h3>
<ul>
<li>You'll receive an email with a link to access your dashboard</li>
<li>After registration closes, you'll be assigned your Secret Santa recipient</li>
<li>You'll receive reminders about important dates</li>
</ul>
<h3>Exchange Details</h3>
<dl>
<dt>Budget</dt>
<dd>{{ exchange.budget }}</dd>
<dt>Gift Exchange Date</dt>
<dd>{{ exchange.exchange_date.strftime('%Y-%m-%d') }}</dd>
</dl>
</section>
</article>
{% endblock %}