diff --git a/src/app/access/[method]/[step]/page.tsx b/src/app/access/[method]/[step]/page.tsx index d09d537..21d40d3 100644 --- a/src/app/access/[method]/[step]/page.tsx +++ b/src/app/access/[method]/[step]/page.tsx @@ -1,7 +1,7 @@ "use client"; import { useState, useTransition, useEffect, use } from "react"; -import { Asterisk, CheckCircle2, Camera, CameraOff, PartyPopper, ExternalLink, ArrowLeft, Copy } from "lucide-react"; +import { Asterisk, CheckCircle2, Camera, CameraOff, PartyPopper, ExternalLink, ArrowLeft, Copy, Gift } from "lucide-react"; import { Button } from "@/components/ui/button"; import { InputOTP, InputOTPGroup, InputOTPSlot } from "@/components/ui/input-otp"; import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group"; @@ -52,6 +52,12 @@ export default function AccessPage({ params }: PageProps) { const [copied, setCopied] = useState(false); const [profileData, setProfileData] = useState<{ avatarUrl: string; gamertag: string } | null>(null); + // Survey State + const [minecraftUsername, setMinecraftUsername] = useState(""); + const [isSubmitting, setIsSubmitting] = useState(false); + const [rewardQueued, setRewardQueued] = useState(false); + const [surveyStatus, setSurveyStatus] = useState<{ ok: boolean; message?: string }>({ ok: false }); + // Loading state to prevent premature redirects const [isLoading, setIsLoading] = useState(true); @@ -62,6 +68,8 @@ export default function AccessPage({ params }: PageProps) { router.push(`/access/${method}/verify`); } else if (step === 'success' && !whitelistSuccess) { router.push(`/access/${method}/verify`); + } else if (step === 'survey' && !whitelistSuccess) { + router.push(`/access/${method}/verify`); } } }, [step, verified, whitelistSuccess, method, router, isLoading]); @@ -91,6 +99,8 @@ export default function AccessPage({ params }: PageProps) { const savedJavaName = localStorage.getItem(`${prefix}java-name`); const savedBedrockName = localStorage.getItem(`${prefix}bedrock-name`); const savedPreference = localStorage.getItem(`${prefix}preference`); + const savedMinecraftUsername = localStorage.getItem(`${prefix}minecraft-username`); + const savedRewardQueued = localStorage.getItem(`${prefix}reward-queued`); if (savedVerified === 'true') { setVerified(true); @@ -110,6 +120,12 @@ export default function AccessPage({ params }: PageProps) { if (savedPreference) { setPreference(savedPreference as "java" | "bedrock" | "both"); } + if (savedMinecraftUsername) { + setMinecraftUsername(savedMinecraftUsername); + } + if (savedRewardQueued === 'true') { + setRewardQueued(true); + } // Fetch profile data for success step if (step === 'success') { @@ -151,6 +167,16 @@ export default function AccessPage({ params }: PageProps) { localStorage.setItem(`${prefix}preference`, preference); }, [preference, method]); + useEffect(() => { + const prefix = method === 'qr' ? 'frg-qr-' : 'frg-'; + localStorage.setItem(`${prefix}minecraft-username`, minecraftUsername); + }, [minecraftUsername, method]); + + useEffect(() => { + const prefix = method === 'qr' ? 'frg-qr-' : 'frg-'; + localStorage.setItem(`${prefix}reward-queued`, rewardQueued.toString()); + }, [rewardQueued, method]); + useEffect(() => { const fetchConfig = async () => { try { @@ -371,17 +397,63 @@ export default function AccessPage({ params }: PageProps) { localStorage.removeItem(`${prefix}java-name`); localStorage.removeItem(`${prefix}bedrock-name`); localStorage.removeItem(`${prefix}preference`); + localStorage.removeItem(`${prefix}minecraft-username`); + localStorage.removeItem(`${prefix}reward-queued`); setVerified(false); setWhitelistSuccess(false); setCode(""); setJavaName(""); setBedrockName(""); + setMinecraftUsername(""); + setRewardQueued(false); setStatus({ ok: false }); router.push(`/access/${method}/verify`); }; + const handleSurveyReward = async () => { + if (!minecraftUsername.trim()) return; + + setIsSubmitting(true); + setSurveyStatus({ ok: false }); + + try { + const response = await fetch("/api/reward", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + player: minecraftUsername, + }), + }); + + const result = await response.json(); + + if (response.ok && result.success) { + setRewardQueued(true); + setSurveyStatus({ + ok: true, + message: result.message || "Successfully queued diamonds!", + }); + } else { + setSurveyStatus({ + ok: false, + message: result.error || "Failed to queue reward", + }); + } + } catch (error) { + console.error("Error queuing reward:", error); + setSurveyStatus({ + ok: false, + message: "We couldn't reach the server. Double-check your connection and try again.", + }); + } finally { + setIsSubmitting(false); + } + }; + // Render different steps if (step === 'verify') { return ( @@ -675,8 +747,8 @@ export default function AccessPage({ params }: PageProps) { {/* Actions */}
@@ -690,6 +762,106 @@ export default function AccessPage({ params }: PageProps) { ); } + if (step === 'survey') { + if (!whitelistSuccess) { + return null; // Redirect handled by useEffect + } + + const TALLY_FORM_URL = "https://tally.so/r/nP5KdL"; + + return ( +
+
+ {/* Header */} +
+ + + Back to success + +

Claim Your Reward

+

+ Help us improve your server experience! Complete this survey to receive 2 diamonds as a thank you. +

+
+ + {/* Reward Section */} +
+ {!rewardQueued ? ( +
+ + setMinecraftUsername(e.target.value)} + className="font-mono" + required + /> +
+ ) : ( +
+ +

2 diamonds queued for {minecraftUsername}!

+
+ )} + + {/* Status */} +
+ {surveyStatus.message && ( +

+ {surveyStatus.ok && } + {surveyStatus.message} +

+ )} +
+ + {/* Actions */} + {!rewardQueued ? ( + + ) : ( + + )} +
+ + {/* Survey Info */} + {!rewardQueued && ( +
+

+ Want to help us improve even more? Take our full survey after claiming your diamonds. +

+ +
+ )} +
+
+ ); + } + // Invalid step - redirect handled by useEffect return null; } diff --git a/src/app/api/reward/route.ts b/src/app/api/reward/route.ts new file mode 100644 index 0000000..727c24a --- /dev/null +++ b/src/app/api/reward/route.ts @@ -0,0 +1,66 @@ +import { NextRequest, NextResponse } from "next/server"; +import { minecraft } from "@/lib/servertap"; + +// Simple in-memory storage for tracking claims +// In production, this should be replaced with a database +const claimedUsers = new Set(); + +export async function POST(request: NextRequest) { + try { + const { player } = await request.json(); + + if (!player || typeof player !== "string" || !player.trim()) { + return NextResponse.json( + { success: false, error: "Valid player name is required" }, + { status: 400 } + ); + } + + const playerName = player.trim().toLowerCase(); + + // Check if player has already claimed diamonds + if (claimedUsers.has(playerName)) { + return NextResponse.json( + { + success: false, + error: "You have already claimed your diamonds! Each player can only claim once." + }, + { status: 409 } + ); + } + + // Call the Minecraft API to reward the player + const result = await minecraft.rewardPlayer(player); + + if (!result.success) { + return NextResponse.json( + { + success: false, + error: result.error || "Failed to reward player" + }, + { status: 500 } + ); + } + + // Mark the player as having claimed + claimedUsers.add(playerName); + + return NextResponse.json({ + success: true, + message: `Successfully queued 2 diamonds for ${player}!`, + }); + } catch (error) { + console.error("Reward endpoint error:", error); + return NextResponse.json( + { success: false, error: "Internal server error" }, + { status: 500 } + ); + } +} + +export async function GET() { + return NextResponse.json( + { error: "Method not allowed" }, + { status: 405 } + ); +} diff --git a/src/app/survey/page.tsx b/src/app/survey/page.tsx index c59f559..19e1349 100644 --- a/src/app/survey/page.tsx +++ b/src/app/survey/page.tsx @@ -4,24 +4,25 @@ import { useState } from "react"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; -import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; -import { ArrowLeft, ExternalLink, Gift } from "lucide-react"; +import { ArrowLeft, ExternalLink, Gift, CheckCircle2 } from "lucide-react"; import Link from "next/link"; export default function SurveyPage() { const [minecraftUsername, setMinecraftUsername] = useState(""); const [isSubmitting, setIsSubmitting] = useState(false); const [rewardQueued, setRewardQueued] = useState(false); + const [status, setStatus] = useState<{ ok: boolean; message?: string }>({ ok: false }); const handleRewardOnly = async (e: React.FormEvent) => { e.preventDefault(); if (!minecraftUsername.trim()) return; setIsSubmitting(true); + setStatus({ ok: false }); try { // Call the reward endpoint to give diamonds - const response = await fetch("/reward", { + const response = await fetch("/api/reward", { method: "POST", headers: { "Content-Type": "application/json", @@ -31,107 +32,122 @@ export default function SurveyPage() { }), }); - if (response.ok) { + const result = await response.json(); + + if (response.ok && result.success) { setRewardQueued(true); + setStatus({ + ok: true, + message: result.message || "Successfully queued diamonds!", + }); } else { - console.error("Failed to queue reward"); + setStatus({ + ok: false, + message: result.error || "Failed to queue reward", + }); } } catch (error) { console.error("Error queuing reward:", error); + setStatus({ + ok: false, + message: "We couldn't reach the server. Double-check your connection and try again.", + }); } finally { setIsSubmitting(false); } }; - const TALLY_FORM_URL = "https://tally.so/r/nP5KdL"; // Replace with your actual Tally form URL + const TALLY_FORM_URL = "https://tally.so/r/nP5KdL"; return ( -
- - -
- -
- FRG Network Survey - +
+
+ {/* Header */} +
+ + + Back to home + +

FRG Network Survey

+

Help us improve your server experience! Complete this survey to receive 2 diamonds as a thank you. - - - - {/* Quick reward section */} -

-
- -

Get Your Diamonds

+

+
+ + {/* Reward Section */} +
+ {!rewardQueued ? ( +
+ + setMinecraftUsername(e.target.value)} + className="font-mono" + required + />
-

- Enter your Minecraft username to queue 2 diamonds for when you join the server: -

- - {!rewardQueued ? ( -
- setMinecraftUsername(e.target.value)} - className="flex-1 font-mono" - required - /> - -
- ) : ( -
- - 2 diamonds queued for {minecraftUsername}! -
+ ) : ( +
+ +

2 diamonds queued for {minecraftUsername}!

+
+ )} + + {/* Status */} +
+ {status.message && ( +

+ {status.ok && } + {status.message} +

)}
- {/* Survey section */} -
-
-

Take the Full Survey

-

- Help us improve the server by sharing your thoughts and preferences. -

-
- - + ) : ( + - -

- Opens in a new window • Takes about 2-3 minutes -

-
+ )} +
- {/* Navigation */} -
-
- - + )} +
); }