fix(join): wire handleJoin and handleSplashJoin in public scripts and join fragments to fix 400 error on /join

This commit is contained in:
Tyler Gillispie 2026-08-27 23:05:18 -07:00
parent 77a82cbe84
commit c221e32855
4 changed files with 133 additions and 13 deletions

View File

@ -1076,3 +1076,116 @@ document.addEventListener("keydown", (e) => {
closeAttendeesDrawer();
}
});
async function handleJoin(e) {
if (e && e.preventDefault) e.preventDefault();
const input = document.getElementById("eventCode");
const code = input ? input.value.trim() : "";
if (!code) return;
const btn = document.getElementById("joinBtn");
const notice = document.getElementById("joinNotice");
if (btn) {
btn.disabled = true;
btn.textContent = "Claiming Seat...";
}
if (notice) notice.style.display = "none";
try {
const res = await fetch("/api/join", {
method: "POST",
headers: {
"Content-Type": "application/json",
"Accept": "application/json",
},
body: JSON.stringify({ code }),
});
const data = await res.json();
if (res.ok && data.success) {
showNotice("Seat claimed! Redirecting...", false);
globalThis.location.href = data.redirectUrl || "/dashboard";
} else {
if (notice) {
notice.textContent = data.error ||
"Invalid event code or workshop capacity reached";
notice.style.display = "block";
notice.style.background = "var(--danger-bg)";
notice.style.color = "var(--danger-text)";
notice.style.border = "1px solid var(--danger-border)";
}
showNotice(data.error || "Invalid event code", true);
if (btn) {
btn.disabled = false;
btn.textContent = "⚡ Enter Workshop";
}
}
} catch (_err) {
if (notice) {
notice.textContent = "Network error connecting to event";
notice.style.display = "block";
notice.style.background = "var(--danger-bg)";
notice.style.color = "var(--danger-text)";
notice.style.border = "1px solid var(--danger-border)";
}
showNotice("Network error connecting to event", true);
if (btn) {
btn.disabled = false;
btn.textContent = "⚡ Enter Workshop";
}
}
}
globalThis.handleJoin = handleJoin;
async function handleSplashJoin(slug) {
const btn = document.getElementById("joinSplashBtn");
const notice = document.getElementById("splashNotice");
if (btn) {
btn.disabled = true;
btn.textContent = "Claiming Seat...";
}
if (notice) notice.style.display = "none";
try {
const res = await fetch("/api/join", {
method: "POST",
headers: {
"Content-Type": "application/json",
"Accept": "application/json",
},
body: JSON.stringify({ code: slug }),
});
const data = await res.json();
if (res.ok && data.success) {
showNotice("Seat claimed! Redirecting...", false);
globalThis.location.href = data.redirectUrl || "/dashboard";
} else {
if (notice) {
notice.textContent = data.error ||
"Invalid event code or workshop is full";
notice.style.display = "block";
notice.style.background = "var(--danger-bg)";
notice.style.color = "var(--danger-text)";
notice.style.border = "1px solid var(--danger-border)";
}
showNotice(data.error || "Failed to enter workshop", true);
if (btn) {
btn.disabled = false;
btn.textContent = "⚡ Enter Workshop & Claim Seat";
}
}
} catch (_err) {
if (notice) {
notice.textContent = "Network error connecting to event";
notice.style.display = "block";
notice.style.background = "var(--danger-bg)";
notice.style.color = "var(--danger-text)";
notice.style.border = "1px solid var(--danger-border)";
}
showNotice("Network error connecting to event", true);
if (btn) {
btn.disabled = false;
btn.textContent = "⚡ Enter Workshop & Claim Seat";
}
}
}
globalThis.handleSplashJoin = handleSplashJoin;

View File

@ -10,7 +10,7 @@ Deno.test("[Events] GET /join returns EventJoinPage HTML", async () => {
assertEquals(res.status, 200);
const html = await res.text();
assertStringIncludes(html, "Join Event or Workshop");
assertStringIncludes(html, 'data-on-submit="@post('/api/join')"');
assertStringIncludes(html, 'onsubmit="handleJoin(event)"');
});
Deno.test("[Events] POST /api/join without code returns HTML error fragment", async () => {

View File

@ -32,7 +32,7 @@ export const EventJoinPageFragment = () => {
</p>
</div>
<form id="joinForm" data-on-submit="@post('/api/join')">
<form id="joinForm" onsubmit="handleJoin(event)">
<div style="margin-bottom: 1.25rem;">
<label style="display: block; font-weight: 600; margin-bottom: 0.35rem; font-size: 0.875rem; color: var(--text-secondary);">
Event PIN or Slug Code
@ -73,6 +73,8 @@ export const EventJoinPageFragment = () => {
</a>
</div>
</div>
<script src="/public/sessions-scripts.js"></script>
</AuthLayoutFragment>
);
};

View File

@ -76,19 +76,22 @@ export const EventSplashPageFragment = ({ event }: { event: any }) => {
)}
</div>
<div
id="splashNotice"
style="display: none; margin-bottom: 1rem; padding: 0.75rem 1rem; border-radius: var(--radius-md); font-size: 0.9rem;"
/>
{!isFull
? (
<form id="splashForm" data-on-submit="@post('/api/join')">
<input type="hidden" name="code" value={event.slug} />
<button
type="submit"
id="joinSplashBtn"
class="btn-primary"
style="width: 100%; min-height: 52px; font-size: 1.05rem; box-shadow: var(--shadow-sm);"
>
Enter Workshop & Claim Seat
</button>
</form>
<button
type="button"
id="joinSplashBtn"
class="btn-primary"
style="width: 100%; min-height: 52px; font-size: 1.05rem; box-shadow: var(--shadow-sm);"
onclick={`handleSplashJoin('${event.slug}')`}
>
Enter Workshop & Claim Seat
</button>
)
: (
<button
@ -111,6 +114,8 @@ export const EventSplashPageFragment = ({ event }: { event: any }) => {
</a>
</div>
</div>
<script src="/public/sessions-scripts.js"></script>
</AuthLayoutFragment>
);
};