Created
December 17, 2024 09:17
-
-
Save Keshav13142/3eeea01eb9be47487f74f9c53a4a1818 to your computer and use it in GitHub Desktop.
Contest invite
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
"use client" | |
import { useState } from "react" | |
import { Button } from "@/components/ui/button" | |
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger } from "@/components/ui/dialog" | |
import { Input } from "@/components/ui/input" | |
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select" | |
import { Checkbox } from "@/components/ui/checkbox" | |
import { Label } from "@/components/ui/label" | |
import { CalendarIcon, UsersIcon, TrophyIcon } from 'lucide-react' | |
import { Card, CardContent } from "@/components/ui/card" | |
// Mock functions and data | |
const validateInviteCode = async (code: string) => { | |
// Simulating API call | |
await new Promise(resolve => setTimeout(resolve, 1000)) | |
return code === "VALID123" | |
} | |
const fetchContestDetails = async (code: string) => { | |
// Simulating API call | |
await new Promise(resolve => setTimeout(resolve, 1000)) | |
return { | |
name: "Summer Coding Challenge", | |
description: "A 3-month long coding contest for all skill levels", | |
startDate: "2023-06-01", | |
endDate: "2023-08-31", | |
memberLimit: 3 | |
} | |
} | |
const userTeams = [ | |
{ id: "1", name: "Alpha Coders" }, | |
{ id: "2", name: "Beta Builders" }, | |
{ id: "3", name: "Gamma Devs" } | |
] | |
const teamMembers = [ | |
{ id: "1", name: "Alice" }, | |
{ id: "2", name: "Bob" }, | |
{ id: "3", name: "Charlie" }, | |
{ id: "4", name: "David" } | |
] | |
type Step = "invite" | "details" | "team" | "members" | |
const useDialogStep = (initialStep: Step) => { | |
const [step, setStep] = useState<Step>(initialStep) | |
const [inviteCode, setInviteCode] = useState("") | |
const [contestDetails, setContestDetails] = useState<any>(null) | |
const [selectedTeam, setSelectedTeam] = useState("") | |
const [selectedMembers, setSelectedMembers] = useState<string[]>([]) | |
return { | |
step, | |
setStep, | |
inviteCode, | |
setInviteCode, | |
contestDetails, | |
setContestDetails, | |
selectedTeam, | |
setSelectedTeam, | |
selectedMembers, | |
setSelectedMembers | |
} | |
} | |
export function ContestInviteDialog() { | |
const [open, setOpen] = useState(false) | |
const { | |
step, | |
setStep, | |
inviteCode, | |
setInviteCode, | |
contestDetails, | |
setContestDetails, | |
selectedTeam, | |
setSelectedTeam, | |
selectedMembers, | |
setSelectedMembers | |
} = useDialogStep("invite") | |
const handleInviteSubmit = async () => { | |
const isValid = await validateInviteCode(inviteCode) | |
if (isValid) { | |
const details = await fetchContestDetails(inviteCode) | |
setContestDetails(details) | |
setStep("details") | |
} else { | |
alert("Invalid invite code") | |
} | |
} | |
const handleTeamSelect = (teamId: string) => { | |
setSelectedTeam(teamId) | |
setStep("members") | |
} | |
const handleMemberSelect = (memberId: string) => { | |
setSelectedMembers(prev => { | |
if (prev.includes(memberId)) { | |
return prev.filter(id => id !== memberId) | |
} else if (prev.length < contestDetails.memberLimit) { | |
return [...prev, memberId] | |
} | |
return prev | |
}) | |
} | |
const handleJoinContest = () => { | |
console.log("Joining contest with:", { inviteCode, selectedTeam, selectedMembers }) | |
setOpen(false) | |
// Reset state | |
setStep("invite") | |
setInviteCode("") | |
setContestDetails(null) | |
setSelectedTeam("") | |
setSelectedMembers([]) | |
} | |
const handleBack = () => { | |
if (step === "details") setStep("invite"); | |
else if (step === "team") setStep("details"); | |
else if (step === "members") setStep("team"); | |
}; | |
return ( | |
<Dialog open={open} onOpenChange={setOpen}> | |
<DialogTrigger asChild> | |
<Button variant="outline">Join Contest</Button> | |
</DialogTrigger> | |
<DialogContent className="sm:max-w-[425px]"> | |
<DialogHeader> | |
<DialogTitle> | |
{step === "invite" && "Enter Invite Code"} | |
{step === "details" && "Contest Details"} | |
{step === "team" && "Select Team"} | |
{step === "members" && "Select Members"} | |
</DialogTitle> | |
</DialogHeader> | |
{step === "invite" && ( | |
<div className="grid gap-4 py-4"> | |
<div className="grid grid-cols-4 items-center gap-4"> | |
<Label htmlFor="invite-code" className="text-right"> | |
Invite Code | |
</Label> | |
<Input | |
id="invite-code" | |
value={inviteCode} | |
onChange={(e) => setInviteCode(e.target.value)} | |
className="col-span-3" | |
/> | |
</div> | |
<Button onClick={handleInviteSubmit}>Submit</Button> | |
</div> | |
)} | |
{step === "details" && contestDetails && ( | |
<div className="grid gap-4 py-4"> | |
<Card> | |
<CardContent className="p-6"> | |
<h3 className="text-lg font-semibold mb-4 flex items-center"> | |
<TrophyIcon className="mr-2 h-5 w-5 text-yellow-500" /> | |
{contestDetails.name} | |
</h3> | |
<p className="text-sm text-gray-500 mb-4">{contestDetails.description}</p> | |
<div className="grid grid-cols-3 gap-4"> | |
<div className="flex items-center"> | |
<CalendarIcon className="mr-2 h-5 w-5 text-blue-500" /> | |
<div> | |
<p className="text-sm font-medium">Start Date</p> | |
<p className="text-sm text-gray-500">{contestDetails.startDate}</p> | |
</div> | |
</div> | |
<div className="flex items-center"> | |
<CalendarIcon className="mr-2 h-5 w-5 text-red-500" /> | |
<div> | |
<p className="text-sm font-medium">End Date</p> | |
<p className="text-sm text-gray-500">{contestDetails.endDate}</p> | |
</div> | |
</div> | |
<div className="flex items-center"> | |
<UsersIcon className="mr-2 h-5 w-5 text-green-500" /> | |
<div> | |
<p className="text-sm font-medium">Member Limit</p> | |
<p className="text-sm text-gray-500">{contestDetails.memberLimit}</p> | |
</div> | |
</div> | |
</div> | |
</CardContent> | |
</Card> | |
<div className="flex justify-between mt-4"> | |
<Button onClick={handleBack} variant="outline">Back</Button> | |
<Button onClick={() => setStep("team")}>Continue</Button> | |
</div> | |
</div> | |
)} | |
{step === "team" && ( | |
<div className="grid gap-4 py-4"> | |
<Select onValueChange={handleTeamSelect}> | |
<SelectTrigger className="w-full"> | |
<SelectValue placeholder="Select a team" /> | |
</SelectTrigger> | |
<SelectContent> | |
{userTeams.map(team => ( | |
<SelectItem key={team.id} value={team.id}>{team.name}</SelectItem> | |
))} | |
</SelectContent> | |
</Select> | |
<div className="flex justify-start mt-4"> | |
<Button onClick={handleBack} variant="outline">Back</Button> | |
</div> | |
</div> | |
)} | |
{step === "members" && ( | |
<div className="grid gap-4 py-4"> | |
<p>Select up to {contestDetails.memberLimit} members:</p> | |
{teamMembers.map(member => ( | |
<div key={member.id} className="flex items-center space-x-2"> | |
<Checkbox | |
id={`member-${member.id}`} | |
checked={selectedMembers.includes(member.id)} | |
onCheckedChange={() => handleMemberSelect(member.id)} | |
disabled={selectedMembers.length >= contestDetails.memberLimit && !selectedMembers.includes(member.id)} | |
/> | |
<label | |
htmlFor={`member-${member.id}`} | |
className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70" | |
> | |
{member.name} | |
</label> | |
</div> | |
))} | |
<div className="flex justify-between mt-4"> | |
<Button onClick={handleBack} variant="outline">Back</Button> | |
<Button onClick={handleJoinContest}>Join Contest</Button> | |
</div> | |
</div> | |
)} | |
</DialogContent> | |
</Dialog> | |
) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment