Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions 2
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
'use client'

import { useEffect, useState } from 'react'
import { QrReader } from 'react-qr-reader'

import { findTeamTicketByPublicId } from '~/server/actions/hackathon'
import { type HackathonTeam } from '~/types/hackathon'

const Page: React.FC = () => {
const [qrData, setQrData] = useState<string | null>(null)
const [loading, setLoading] = useState(false)
const [teamData, setTeamData] = useState<HackathonTeam | null>(null)

useEffect(() => {
if (qrData) {
findTeamTicketByPublicId(qrData)
.then((res) => {
console.log(res)
})
.catch((err: unknown) => {
console.error(err)
})
}
}, [qrData])

return (
<div className='min-h-dvh w-full'>
<div className='relative mx-auto w-full max-w-screen-sm'>
<div className='absolute left-1/2 top-1/2 z-10 aspect-square w-1/2 -translate-x-1/2 -translate-y-1/2 rounded-3xl border-[3px] border-white/50' />
<QrReader
className='aspect-square w-full'
constraints={{}}
onResult={(result) => {
if (result) {
setQrData(result.getText())
}
}}
/>
<p>{qrData}</p>
</div>
</div>
)
}

export default Page
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@
"react-dom": "^18.3.1",
"react-hook-form": "^7.53.1",
"react-intersection-observer": "^9.15.1",
"react-qr-reader": "3.0.0-beta-1",
"react-responsive": "^10.0.0",
"react-responsive-carousel": "^3.2.23",
"react-scroll": "^1.9.0",
Expand Down
652 changes: 396 additions & 256 deletions pnpm-lock.yaml

Large diffs are not rendered by default.

125 changes: 125 additions & 0 deletions src/app/(events)/hackathon/(admin)/team/search/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
'use client'

import { useEffect, useState } from 'react'
import { QrReader } from 'react-qr-reader'

import { Button } from '~/components/ui/button'
import { cn } from '~/lib/utils'
import { findTeamTicketByPublicId } from '~/server/actions/hackathon'
import { type HackathonTeam } from '~/types/hackathon'

const Page: React.FC = () => {
const [qrData, setQrData] = useState<string | null>(null)
const [loading, setLoading] = useState(false)
const [error, setError] = useState<string | null>(null)
const [teamData, setTeamData] = useState<HackathonTeam | null>(null)

useEffect(() => {
if (qrData) {
setLoading(true)
setError(null)
setTeamData(null)

console.log(`Searching Team Ticket for: ${qrData}`)

findTeamTicketByPublicId(qrData)
.then((res) => {
if (!res.success) {
setError(res.message ?? null)
console.log(res.errors)
return
}
setTeamData(res.data)
})
.catch((err: unknown) => {
console.error(err)
})
.finally(() => {
setLoading(false)
})
}
}, [qrData])

return (
<div className='flex min-h-dvh w-full flex-col gap-4 px-4 pb-6'>
<div className='relative mx-auto w-full max-w-screen-sm'>
<div
className={cn(
'absolute left-1/2 top-1/2 z-10 aspect-square w-1/2 -translate-x-1/2 -translate-y-1/2 rounded-3xl border-[3px]',
loading ? 'border-green-400/80' : 'border-white/50'
)}
/>
<QrReader
className='aspect-square w-full'
constraints={{
facingMode: 'environment',
}}
onResult={(result) => {
if (result) {
setQrData(result.getText())
}
}}
/>
</div>
{error ? <p>{error}</p> : null}
{loading ? <p>Searching...</p> : null}
{teamData ? (
<div className='flex w-full flex-col'>
<div className='mb-4 w-full border-collapse text-left'>
<div className='w-full border p-2 font-bold'>Team Information</div>
<TableRow field='Team Name' value={teamData.teamName} />
<TableRow field='Team ID' value={teamData.id.toString()} />
<TableRow field='Team Public ID' value={teamData.publicId} />
</div>
{teamData.teamMembers.map((teamMember, idx) => (
<div
key={teamMember.id}
className='mb-4 w-full border-collapse text-left'
>
<div className='w-full border p-2 font-bold'>
Member {idx + 1}
</div>
<TableRow field='First Name' value={teamMember.firstName} />
<TableRow field='Last Name' value={teamMember.lastName} />
<TableRow field='Nickname' value={teamMember.nickname} />
<TableRow field='Pronoun' value={teamMember.pronoun} />
<TableRow field='Email' value={teamMember.email} />
<TableRow field='Phone Number' value={teamMember.phoneNumber} />
<TableRow field='Student ID' value={teamMember.studentId} />
<TableRow field='Faculty' value={teamMember.faculty} />
<TableRow field='Department' value={teamMember.department} />
<TableRow field='University' value={teamMember.university} />
<TableRow field='Role' value={teamMember.role} />
</div>
))}
<Button
className='mt-2 w-full'
onClick={() => {
setQrData(null)
setTeamData(null)
}}
>
Clear
</Button>
</div>
) : null}
</div>
)
}

export default Page

interface TableRowProps {
field: string
value: string
className?: string
}

const TableRow: React.FC<TableRowProps> = ({ field, value, className }) => {
return (
<div className={cn('grid grid-cols-8', className)}>
<div className='col-span-3 border p-2'>{field}</div>
<div className='col-span-5 border p-2'>{value}</div>
</div>
)
}
17 changes: 0 additions & 17 deletions src/app/(events)/hackathon/admin/layout.tsx

This file was deleted.

40 changes: 0 additions & 40 deletions src/app/(events)/hackathon/admin/page.tsx

This file was deleted.

62 changes: 62 additions & 0 deletions src/server/actions/hackathon.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,10 @@ import {
type CreateHackathonTeamMemberInput,
type HackathonCommunityRegistration,
type HackathonCommunityTeamMember,
type HackathonCommunityTeamTicket,
type HackathonRegistration,
type HackathonSpinResult,
type HackathonTeam,
type HackathonTeamMember,
type HackathonTeamTicket,
type HackathonTicket,
Expand Down Expand Up @@ -294,3 +296,63 @@ export async function getCommunityRegistrationByCode(
code,
})
}

export async function findTeamTicketByPublicId(
publicId: string
): Promise<Response<HackathonTeam>> {
let res = null as
| (HackathonTeamTicket & {
registration: HackathonRegistration | null
teamMembers: HackathonTeamMember[]
})
| (HackathonCommunityTeamTicket & {
teamMembers: HackathonTeamMember[]
})
| null
let teamName = ''

try {
const resTeamTicket = await api.hackathon.findTeamByPublicId({ publicId })
if (!resTeamTicket.success) {
throw new Error('')
} else if (resTeamTicket.data && resTeamTicket.data.registration) {
res = resTeamTicket.data
teamName = resTeamTicket.data.registration.teamName
}

if (!resTeamTicket.data) {
const resCommunityTeamTicket =
await api.hackathon.findCommunityTeamByPublicId({ publicId })
if (!resCommunityTeamTicket.success) {
throw new Error('')
} else if (resCommunityTeamTicket.data) {
res = resCommunityTeamTicket.data
teamName = resCommunityTeamTicket.data.teamName
}
}
} catch (error) {
return {
success: false,
message: 'Error fetching team ticket',
errors: ['Error fetching team ticket'],
}
}

if (!res) {
return {
success: false,
message: 'Team ticket not found',
errors: ['Team ticket not found'],
}
}

return {
success: true,
data: {
id: res.id,
publicId: res.publicId,
teamMembers: res.teamMembers,
teamName,
},
}
}
4 changes: 4 additions & 0 deletions src/server/api/dto/hackathon.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,3 +100,7 @@ export const GetHackathonCommunityRegistrationByCodeDto = z.object({
export const GetRegistrationIndexByCommunityCodeDto = z.object({
code: z.string(),
})

export const GetTeamByPublicId = z.object({
publicId: z.string(),
})
Loading