move org request form and link
All checks were successful
Build Images and Deploy / Update-PROD-Stack (push) Successful in 29s

This commit is contained in:
2026-03-20 22:06:59 -04:00
parent 08b53d0e39
commit 5ac00d2ff1
4 changed files with 52 additions and 17 deletions

View File

@@ -158,33 +158,40 @@ router.post('/player/:username/display-name', requireAuth, (req, res) => {
});
// ─── Apply to become organizer ────────────────────────────
router.post('/player/:username/apply-organizer', requireAuth, (req, res) => {
const user = Users.findByUsername(req.params.username);
if (!user || user.id !== req.session.userId) {
return res.status(403).render('error', { title: 'Forbidden', message: 'You can only submit your own application.' });
router.get('/apply-organizer', requireAuth, (req, res) => {
const user = Users.findById(req.session.userId);
if (user.is_organizer || user.is_admin) {
req.session.flash = { type: 'info', message: 'You already have organizer access.' };
return res.redirect(`/player/${user.username}`);
}
const pendingApplication = OrganizerApplications.findByUser(user.id);
res.render('apply-organizer', { title: 'Apply to Become an Organizer', pendingApplication });
});
router.post('/apply-organizer', requireAuth, (req, res) => {
const user = Users.findById(req.session.userId);
if (user.is_organizer || user.is_admin) {
req.session.flash = { type: 'info', message: 'You already have organizer access.' };
return res.redirect(`/player/${user.username}`);
}
if (OrganizerApplications.findByUser(user.id)) {
req.session.flash = { type: 'info', message: 'You already have a pending application.' };
return res.redirect(`/player/${user.username}`);
return res.redirect('/apply-organizer');
}
const reason = (req.body.reason || '').trim();
if (!reason || reason.length < 10) {
req.session.flash = { type: 'danger', message: 'Please provide a reason (at least 10 characters).' };
return res.redirect(`/player/${user.username}`);
return res.redirect('/apply-organizer');
}
if (reason.length > 1000) {
req.session.flash = { type: 'danger', message: 'Reason is too long (max 1000 characters).' };
return res.redirect(`/player/${user.username}`);
return res.redirect('/apply-organizer');
}
OrganizerApplications.submit(user.id, reason);
req.session.flash = { type: 'success', message: 'Your organizer application has been submitted!' };
res.redirect(`/player/${user.username}`);
res.redirect('/apply-organizer');
});
// ─── Delete own account ───────────────────────────────────

View File

@@ -67,6 +67,7 @@
<li>Download printable PDF cards with QR codes (Avery 5371 format, 10 per page)</li>
<li>Monitor scan activity and leaderboards for your hunts</li>
</ul>
<p style="margin-top: 1rem;">Interested? <a href="/apply-organizer" style="color: var(--primary); font-weight: 600;">Apply to become an organizer</a>.</p>
</div>
<div class="card" style="margin-top: 1.5rem;">

View File

@@ -0,0 +1,31 @@
<%- include('partials/header') %>
<div class="container">
<h1>Apply to Become an Organizer</h1>
<div class="card" style="margin-top: 1.5rem;">
<p>Organizers can create treasure hunts, generate QR code cards, set start dates and expiry windows, and monitor their hunt activity and leaderboards.</p>
<p>If you'd like to run your own hunts, fill out the form below. An admin will review your application.</p>
</div>
<% if (typeof pendingApplication !== 'undefined' && pendingApplication) { %>
<div class="card" style="margin-top: 1.5rem;">
<div class="card-header">&#x23F3; Application Pending</div>
<p style="color: var(--muted); font-size: 0.9rem;">Your application is pending review. Hang tight!</p>
<p style="font-size: 0.85rem; padding: 0.75rem; background: var(--body-bg); border-radius: 6px; color: var(--muted);"><%= pendingApplication.reason %></p>
</div>
<% } else { %>
<div class="card" style="margin-top: 1.5rem;">
<form method="POST" action="/apply-organizer">
<div class="form-group">
<label for="reason">Why do you want to become an organizer?</label>
<textarea id="reason" name="reason" class="form-control" rows="5" required minlength="10" maxlength="1000" placeholder="I'd like to organize a hunt because..."></textarea>
<div class="form-hint">10&ndash;1000 characters.</div>
</div>
<button type="submit" class="btn btn-primary">Submit Application</button>
</form>
</div>
<% } %>
</div>
<%- include('partials/footer') %>

View File

@@ -93,16 +93,12 @@
<div class="card-header">&#x1F3AF; Become an Organizer</div>
<% if (typeof pendingApplication !== 'undefined' && pendingApplication) { %>
<p style="color: var(--muted); font-size: 0.9rem;">Your application is pending review. Hang tight!</p>
<p style="font-size: 0.85rem; padding: 0.75rem; background: var(--body-bg); border-radius: 6px; color: var(--muted);"><%= pendingApplication.reason %></p>
<% } else { %>
<p style="color: var(--muted); font-size: 0.9rem;">Organizers can create hunts, generate QR codes, and manage their own events. Tell us why you'd like to become one!</p>
<form method="POST" action="/player/<%= profile.username %>/apply-organizer">
<div class="form-group">
<label for="reason">Why do you want to become an organizer?</label>
<textarea id="reason" name="reason" class="form-control" rows="4" required minlength="10" maxlength="1000" placeholder="I'd like to organize a hunt because..."></textarea>
</div>
<button type="submit" class="btn btn-primary btn-sm">Submit Application</button>
</form>
<p style="color: var(--muted); font-size: 0.9rem;">Want to create your own treasure hunts?</p>
<% } %>
<a href="/apply-organizer" class="btn btn-primary btn-sm"><%= (typeof pendingApplication !== 'undefined' && pendingApplication) ? 'View Application' : 'Apply Now' %></a>
</div>
<% } %>
<% } %>
</div>
<% } %>