Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Hacker Check-In #54

Merged
merged 19 commits into from
Jan 17, 2024
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
4 changes: 2 additions & 2 deletions .github/workflows/firebase-hosting-merge-development.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ jobs:
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 16
node-version: 18
- name: Install Dependencies
run: yarn install --frozen-lockfile
- name: Create Production Build
Expand All @@ -39,7 +39,7 @@ jobs:
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 16
node-version: 18
- name: Install Dependencies
run: yarn install --frozen-lockfile
- name: Lint
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/firebase-hosting-merge-production.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ jobs:
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 16
node-version: 18
- name: Install Dependencies
run: yarn install --frozen-lockfile
- name: Create Production Build
Expand All @@ -39,7 +39,7 @@ jobs:
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 16
node-version: 18
- name: Install Dependencies
run: yarn install --frozen-lockfile
- name: Lint
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/firebase-hosting-pull-request.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ jobs:
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 16
node-version: 18
- name: Install Dependencies
run: yarn install --frozen-lockfile
- name: Create Production Build
Expand All @@ -36,7 +36,7 @@ jobs:
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 16
node-version: 18
- name: Install Dependencies
run: yarn install --frozen-lockfile
- name: Lint
Expand Down
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,9 @@
"react-dom": "^18.2.0",
"react-hook-form": "^7.47.0",
"react-hot-toast": "^2.4.1",
"react-qr-code": "^2.0.12",
"react-router-dom": "^6.6.1",
"react-zxing": "^2.0.0",
"recharts": "^2.9.3",
"zod": "^3.22.4"
},
Expand Down
7 changes: 7 additions & 0 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ import ScrollToAnchor from "./components/scrollControl/ScrollToAnchor"
import Team from "./views/team"
import ApplicationClosed from "./views/ApplicationClosed"

import QRCheckIn from "./views/portal/admin/QRCheckIn"
import DashbaordHacker from "./views/portal/hacker/dashboard"
import QRCodeHacker from "./views/portal/hacker/QRCode"

const App: React.FC = () => {
const {
auth: { loading },
Expand Down Expand Up @@ -107,7 +111,9 @@ const App: React.FC = () => {

<Route element={<RoleProtectedRoute allowedRole='hacker' />}>
<Route path='portal/hacker' element={<HackerPortal />}>
<Route index element={<DashbaordHacker />} />
{/* Hacker Portal sub-routes go here*/}
<Route path='check-in' element={<QRCodeHacker />} />
<Route path='teams' element={<TeamsHacker />} />
</Route>
</Route>
Expand All @@ -121,6 +127,7 @@ const App: React.FC = () => {
/>
<Route path='teams' element={<TeamAdmin />} />
<Route path='users' element={<UsersAdmin />} />
<Route path='check-in' element={<QRCheckIn />} />
</Route>
</Route>

Expand Down
34 changes: 34 additions & 0 deletions src/utils/apis/cloudFunctions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -290,3 +290,37 @@ export const getStatistics = async () => {
throw err as Error
}
}

/**
* CruzHacks-2024-Backend API endpoint for checking in a user
*/
export const checkInUser = async (userSession: User, uid: string) => {
try {
const idToken = await userSession.getIdToken(false)

const response = await axios.post(`${API_URL}/auth/checkIn`, null, {
params: {
uid,
},
headers: {
Authorization: `Bearer ${idToken}`,
},
})

const { data, error } = response.data

if (error) throw new Error(error)

return data.userRecord as User
} catch (err) {
if (isAxiosError(err)) {
if (err.response?.data?.error) {
console.error(err.response.data.error)
throw new Error(err.response.data.error)
}
console.error(err)
throw new Error(err.message)
}
throw err as Error
}
}
106 changes: 106 additions & 0 deletions src/views/portal/admin/QRCheckIn/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
import React from "react"
import { useZxing } from "react-zxing"
import { checkInUser } from "../../../../utils/apis/cloudFunctions"
import toast from "react-hot-toast"
import { classNames, isString } from "../../../../utils/string"
import { CameraIcon } from "@heroicons/react/24/outline"
import type { User } from "firebase/auth"
import useAuth from "../../../../hooks/useAuth"

const QRCheckInContainer: React.FC = () => {
const {
auth: { user },
} = useAuth()

const { ref } = useZxing({
onDecodeResult: result => {
console.log(result)
handleScanUID(result.getText())
},
timeBetweenDecodingAttempts: 50,
onError: error => {
console.error(error)
},
})
const [lastScanned, setLastScanned] = React.useState<User>()

const handleScanUID = async (result: string) => {
try {
if (!user) throw "No user session found"
const uid = result
const hacker = await checkInUser(user, uid)

setLastScanned(hacker)
toast.success(`Successfully checked in ${hacker.displayName}`)
} catch (error) {
if (error && isString((error as Error).message)) {
const errorMessage = (error as Error).message
toast.error(errorMessage)
}
}
}

return (
<div className='space-y-10 overflow-x-clip px-4 sm:px-6 lg:px-8'>
<div className='space-y-3'>
<h1 className='font-title text-2xl font-semibold leading-6'>
Check-In QR Code Scanner
</h1>
<p className=''>
Scan a Hacker&apos;s QR Code in the camera box below to check them in
at the beginning of the event.
</p>
</div>

<div className='flex w-full min-w-0 flex-col items-center justify-center gap-5 md:flex-row md:items-stretch'>
<div className='flex w-fit max-w-80 flex-col items-center justify-center gap-3 rounded-3xl bg-[#4659FF]/10 p-5 md:p-10'>
{/* eslint-disable-next-line jsx-a11y/media-has-caption */}
<video
ref={ref}
className='aspect-square h-auto w-full bg-blue-imperial'
/>

<p className='w-full max-w-80 text-center font-subtext text-sm text-white/70'>
When prompted, make sure to enable{" "}
<CameraIcon className='-mt-0.5 inline-block h-4 w-4' /> permissions.
</p>
</div>
{lastScanned && (
<div className='flex w-full max-w-80 flex-col gap-3 rounded-3xl bg-[#4659FF]/10 p-5 md:max-w-none md:p-10'>
<h2 className='font-title'>Last Scanned</h2>
<ul className='list-disc space-y-3 ps-8'>
<li className='font-subtext font-semibold'>
Name:{" "}
<span
className={classNames(
!lastScanned.displayName
? "italic text-pink/70"
: "text-pink",
"font-normal"
)}
>
{lastScanned.displayName ?? "< NO NAME >"}
</span>
</li>
<li className='font-subtext font-semibold'>
Email:{" "}
<span
className={classNames(
!lastScanned.email ? "italic text-pink/90" : "text-pink",
"font-normal"
)}
>
{lastScanned.email ?? "< NO NAME >"}
</span>
</li>
</ul>
</div>
)}
</div>
</div>
)
}

const QRCheckIn = () => <QRCheckInContainer />

export default QRCheckIn
6 changes: 6 additions & 0 deletions src/views/portal/admin/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
NewspaperIcon,
UserGroupIcon,
UsersIcon,
CameraIcon,
} from "@heroicons/react/24/outline"

const nav = [
Expand All @@ -28,6 +29,11 @@ const nav = [
href: "/portal/admin/teams",
icon: UserGroupIcon,
},
{
name: "QR Check-In",
href: "/portal/admin/check-in",
icon: CameraIcon,
},
]

const PortalAdmin = () => {
Expand Down
38 changes: 38 additions & 0 deletions src/views/portal/hacker/QRCode/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import React from "react"
import QRCode from "react-qr-code"
import useAuth from "../../../../hooks/useAuth"

const QRCodeHacker = () => {
const {
auth: { user },
} = useAuth()

return (
<div className='space-y-10 overflow-x-clip px-4 sm:px-6 lg:px-8'>
<div className='sm:flex sm:items-center'>
<div className='sm:flex-auto'>
<h1 className='font-title text-2xl font-semibold leading-6'>
Check-In QR Code
</h1>
<p className='text-gray-700 mt-2 text-sm'>
Present this QR code to a CruzHacks Organizer to check-in to the
event.
</p>
</div>
</div>

<div className='flex w-full items-center justify-center md:justify-start'>
<div className='flex w-full items-center justify-center rounded-3xl bg-[#4659FF]/10 p-5 md:w-fit md:space-y-10 md:p-10'>
{user ? (
<QRCode value={user.uid} />
) : (
// SHOULD NEVER HAPPEN
<p className='text-error'>No User</p>
)}
</div>
</div>
</div>
)
}

export default QRCodeHacker
16 changes: 16 additions & 0 deletions src/views/portal/hacker/dashboard/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import React from "react"
// import useAuth from "../../../../hooks/useAuth"

const DashbaordHacker = () => {
// const {
// auth: { user },
// } = useAuth()

return (
<div>
<h1 className='font-title text-3xl'>Dashboard</h1>
</div>
)
}

export default DashbaordHacker
18 changes: 6 additions & 12 deletions src/views/portal/hacker/index.tsx
Original file line number Diff line number Diff line change
@@ -1,24 +1,18 @@
import React from "react"
import Sidebar from "../Sidebar"
import { HomeIcon } from "@heroicons/react/24/solid"
import { UserGroupIcon } from "@heroicons/react/24/outline"
import { CameraIcon, HomeIcon , UserGroupIcon } from "@heroicons/react/24/outline"

const nav = [
{
name: "Dashboard",
href: "/portal/hacker",
icon: HomeIcon,
},
// {
// name: "Applications",
// href: "/portal/admin/applications",
// icon: NewspaperIcon,
// },
// {
// name: "Users",
// href: "/portal/admin/users",
// icon: UsersIcon,
// },
{
name: "Check In",
href: "/portal/hacker/check-in",
icon: CameraIcon,
},
{
name: "Teams",
href: "/portal/hacker/teams",
Expand Down
1 change: 0 additions & 1 deletion src/views/portal/hacker/teams/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@ const TeamHacker = () => {
}, [])

useEffect(() => {
console.log("fromt useEffect", teamPage)
teamPage.teamName !== "" ? setTeamStatus("INTEAM") : setTeamStatus("JOIN")
}, [teamPage])

Expand Down
Loading
Loading