From 12fc7988c10a5a92e2d45623d7e1dd26436f8c8e Mon Sep 17 00:00:00 2001 From: Enzo Vieira Date: Wed, 24 Jan 2024 21:57:44 +0000 Subject: [PATCH] refactor(AuthContext): Refactor to TypeScript and change dir (#404) * Rebase (squashed Enzo's commits) * Change import to lib alias * Improve login/logout logic * Fix lint error * Remove withoutAuth * Add types to AuthContext * Fix type errors --------- Co-authored-by: tiago-bacelar --- .../Auth/{AuthProvider.js => AuthContext.tsx} | 181 +++++++++++++----- context/Auth/index.js | 4 - context/Auth/index.ts | 13 ++ context/Auth/useAuth.js | 4 - context/Auth/withAuth.js | 13 +- context/Auth/withoutAuth.js | 16 -- generator/templates/layouts/layout.tsx.hbs | 9 +- layout/Attendee/Badgedex/Badgedex.tsx | 20 +- layout/Attendee/Identifier/Identifier.tsx | 4 +- layout/Attendee/Inventory/Inventory.tsx | 4 +- layout/Attendee/Profile/Profile.tsx | 9 +- layout/Attendee/Store/Store.tsx | 4 +- layout/Attendee/Wheel/Wheel.tsx | 7 +- layout/Challenges/Challenges.tsx | 4 +- layout/FAQs/FAQs.tsx | 4 +- layout/ForgotPassword/ForgotPassword.tsx | 4 +- layout/Hackathon/Hackathon.tsx | 4 +- layout/Home/Home.tsx | 4 +- layout/Login/Login.tsx | 16 +- layout/Product/Product.tsx | 10 +- layout/ResetPassword/ResetPassword.tsx | 4 +- layout/Schedule/Schedule.tsx | 4 +- layout/SignUp/SignUp.tsx | 4 +- layout/Speakers/Speakers.tsx | 3 +- layout/Sponsor/Scanner/Scanner.tsx | 4 +- layout/Staff/Prizes/Prizes.tsx | 6 +- layout/Team/Team.tsx | 4 +- pages/register/[uuid].js | 4 +- 28 files changed, 219 insertions(+), 148 deletions(-) rename context/Auth/{AuthProvider.js => AuthContext.tsx} (51%) delete mode 100644 context/Auth/index.js create mode 100644 context/Auth/index.ts delete mode 100644 context/Auth/useAuth.js delete mode 100644 context/Auth/withoutAuth.js diff --git a/context/Auth/AuthProvider.js b/context/Auth/AuthContext.tsx similarity index 51% rename from context/Auth/AuthProvider.js rename to context/Auth/AuthContext.tsx index a9176063..1c3b0a40 100644 --- a/context/Auth/AuthProvider.js +++ b/context/Auth/AuthContext.tsx @@ -1,13 +1,95 @@ -import { createContext, useEffect, useMemo, useState } from "react"; +import { createContext, useContext, useEffect, useState } from "react"; import { useRouter } from "next/router"; -import API from "/lib/api"; -import * as api from "/lib/api"; -import * as USER from "/lib/user"; +import API from "@lib/api"; +import * as api from "@lib/api"; +import * as USER from "@lib/user"; -export const AuthContext = createContext(); +interface ILoginDTO { + email: string; + password: string; +} + +export interface IBadge { + avatar: string | null; + begin: string; + description: string; + end: string; + id: number; + name: string; + tokens: number; + type: number; +} + +export interface IPrize { + avatar: string; + id: number; + is_redeemable: boolean; + name: string; + not_redeemed: number; +} + +export interface IAbstractUser { + email: string; + type: string; +} + +export interface IAttendee extends IAbstractUser { + avatar: string | null; + badge_count: number; + badges: IBadge[]; + course: number; + cv: string | null; + entries: number; + id: string; + name: string; + nickname: string; + prizes: IPrize[]; + redeemables: IPrize[]; + token_balance: number; +} + +export interface IStaff extends IAbstractUser { + id: number; +} + +export interface ISponsor extends IAbstractUser { + badge_id: number; + /** The id of the company */ + id: number; + name: string; + sponsorship: string; + /** The id of the user */ + user_id: number; +} + +export type IUser = IAttendee | ISponsor | IStaff; + +interface IAuthContext { + user: IUser | null; + errors?: string; + + // Booleans + isAuthenticated: boolean; + isLoading: boolean; + + // Updates user in state + updateUser: (user: IUser | null) => void; + refetchUser: () => void; + + // Api calls + login: (params: ILoginDTO) => void; + logout: () => void; + editUser: (username: FormData) => void; + sign_up: (fields: any) => void; +} + +export const AuthContext = createContext({} as IAuthContext); const TOKEN_KEY_NAME = "safira_token"; +// Function that consumes the context +export const useAuth = () => useContext(AuthContext); + export function AuthProvider({ children }) { const router = useRouter(); const [user, setUser] = useState(null); @@ -19,6 +101,7 @@ export function AuthProvider({ children }) { useEffect(() => { if (user) { + console.log(user); setAuthenticated(true); } else { setAuthenticated(false); @@ -44,7 +127,7 @@ export function AuthProvider({ children }) { }) .catch((_errors) => { // It means the jwt is expired - localStorage.clear(); + localStorage.removeItem(TOKEN_KEY_NAME); delete API.defaults.headers.common["Authorization"]; }) .finally(() => setFirstLoading(false)); @@ -77,38 +160,41 @@ export function AuthProvider({ children }) { }) .catch((e) => { setErrors("Something went wrong. Check your input and try again"); //e.message - setUser(undefined); + setUser(null); setLoading(false); }); } function login({ email, password }) { setLoading(true); + api .sign_in({ email, password }) .then(({ jwt }) => { localStorage.setItem(TOKEN_KEY_NAME, jwt); API.defaults.headers.common["Authorization"] = `Bearer ${jwt}`; - api.getCurrentUser().then((response) => { - setUser(response); - switch (response.type) { - case USER.ROLES.ATTENDEE: - router.push("/attendee/profile"); - break; - case USER.ROLES.SPONSOR: - router.push("/sponsor/scanner"); - break; - case USER.ROLES.STAFF: - router.push("/staff/badges"); - break; - default: - throw new Error(`Unknown USER TYPE: ${response.type}`); + api.getCurrentUser().then(async (response) => { + if (router.query.from == undefined) { + switch (response.type) { + case USER.ROLES.ATTENDEE: + await router.push({ query: { from: "/attendee/profile" } }); + break; + case USER.ROLES.SPONSOR: + await router.push({ query: { from: "/sponsor/scanner" } }); + break; + case USER.ROLES.STAFF: + await router.push({ query: { from: "/staff/badges" } }); + break; + } } + setUser(response); }); }) .catch((errors) => { - if (errors.response) { - setErrors("Invalid credentials"); + if (errors.response?.data?.error) { + setErrors(errors.response.data.error); + } else if (errors.response) { + setErrors("Request denied by the server"); } else if (errors.request) { setErrors( "No connection to the server. Please check your internet connection and try again later" @@ -118,30 +204,28 @@ export function AuthProvider({ children }) { "Something went wrong :/ Please check your internet connection and try again later" ); } - - setUser(undefined); + setUser(null); setLoading(false); }); } function logout() { setLoading(true); - localStorage.clear(); + localStorage.removeItem(TOKEN_KEY_NAME); delete API.defaults.headers.common["Authorization"]; - setUser(undefined); - router.push("/"); + router.push("/").finally(() => setUser(null)); } - function editUser(formData) { + function editUser(nickname) { setLoading(true); api - .editUser(user.id, formData) + .editUser(user.id, nickname) .then((at) => { setUser((oldUser) => ({ ...oldUser, ...at })); }) .catch((errors) => { - setUser(undefined); + setUser(null); setErrors( "Something went wrong :/ Please check your internet connection and try again later" ); @@ -152,26 +236,25 @@ export function AuthProvider({ children }) { setRefetch((needsRefetch) => !needsRefetch); } - // Make the provider update only when it should - const values = useMemo( - () => ({ - user, - isAuthenticated, - isLoading, - setUser, - errors, - login, - logout, - editUser, - refetchUser, - sign_up, - }), - // eslint-disable-next-line - [user, isAuthenticated, isLoading, errors] - ); + function updateUser(updatedUser: IUser | null) { + setUser(updatedUser); + } return ( - + {!isFirstLoading && children} ); diff --git a/context/Auth/index.js b/context/Auth/index.js deleted file mode 100644 index b130f51e..00000000 --- a/context/Auth/index.js +++ /dev/null @@ -1,4 +0,0 @@ -export { AuthProvider } from "./AuthProvider"; -export { useAuth } from "./useAuth"; -export { withAuth } from "./withAuth"; -export { withoutAuth } from "./withoutAuth"; diff --git a/context/Auth/index.ts b/context/Auth/index.ts new file mode 100644 index 00000000..7847d76f --- /dev/null +++ b/context/Auth/index.ts @@ -0,0 +1,13 @@ +export type { + IBadge, + IPrize, + IAbstractUser, + IAttendee, + IStaff, + ISponsor, + IUser, +} from "./AuthContext"; + +export { AuthProvider } from "./AuthContext"; +export { useAuth } from "./AuthContext"; +export { withAuth } from "./withAuth"; diff --git a/context/Auth/useAuth.js b/context/Auth/useAuth.js deleted file mode 100644 index 37766675..00000000 --- a/context/Auth/useAuth.js +++ /dev/null @@ -1,4 +0,0 @@ -import { useContext } from "react"; -import { AuthContext } from "./AuthProvider"; - -export const useAuth = () => useContext(AuthContext); diff --git a/context/Auth/withAuth.js b/context/Auth/withAuth.js index dcf2f627..bec7d993 100644 --- a/context/Auth/withAuth.js +++ b/context/Auth/withAuth.js @@ -1,5 +1,5 @@ import { useRouter } from "next/router"; -import { useAuth } from "./useAuth"; +import { useAuth } from "."; import * as USER from "/lib/user"; export function withAuth(WrappedComponent) { @@ -9,7 +9,7 @@ export function withAuth(WrappedComponent) { const { user } = useAuth(); if (!user) { - router.replace("/signup"); + router.replace(`/login?from=${encodeURIComponent(router.asPath)}`); return null; } @@ -29,7 +29,8 @@ export function withAuth(WrappedComponent) { "/product/[slug]", ].includes(router.pathname) ) { - return router.replace("/404"); + router.replace("/404"); + return null; } break; case USER.ROLES.STAFF: @@ -43,7 +44,8 @@ export function withAuth(WrappedComponent) { "/attendees/[uuid]", ].includes(router.pathname) ) { - return router.replace("/404"); + router.replace("/404"); + return null; } break; case USER.ROLES.SPONSOR: @@ -55,7 +57,8 @@ export function withAuth(WrappedComponent) { "/sponsor/visitors", ].includes(router.pathname) ) { - return router.replace("/404"); + router.replace("/404"); + return null; } break; } diff --git a/context/Auth/withoutAuth.js b/context/Auth/withoutAuth.js deleted file mode 100644 index 5c9822fe..00000000 --- a/context/Auth/withoutAuth.js +++ /dev/null @@ -1,16 +0,0 @@ -import { faUserAstronaut } from "@fortawesome/free-solid-svg-icons"; -import { Router, useRouter } from "next/router"; -import { useAuth } from "./useAuth"; - -export function withoutAuth(WrappedComponent) { - // eslint-disable-next-line react/display-name - return (props) => { - const router = useRouter(); - const { user } = useAuth(); - - if (user && router.pathname == "/login") { - router.replace("/"); - } - return ; - }; -} diff --git a/generator/templates/layouts/layout.tsx.hbs b/generator/templates/layouts/layout.tsx.hbs index 9a61fd4c..29b3e35c 100644 --- a/generator/templates/layouts/layout.tsx.hbs +++ b/generator/templates/layouts/layout.tsx.hbs @@ -1,6 +1,7 @@ -import { -{{#if shouldAddAuth}}withAuth{{else}}withoutAuth{{/if}} -} from "@context/Auth"; + +{{#if shouldAddAuth}} +import { withAuth } from "@context/Auth"; +{{/if}} {{#if shouldAddComponents}} import { Component } from "./components"; @@ -14,4 +15,4 @@ type

{{pascalCase name}}

); } export default -{{#if shouldAddAuth}}withAuth{{else}}withoutAuth{{/if}}({{pascalCase name}}); \ No newline at end of file +{{#if shouldAddAuth}}withAuth({{pascalCase name}}){{else}}{{pascalCase name}}{{/if}}; \ No newline at end of file diff --git a/layout/Attendee/Badgedex/Badgedex.tsx b/layout/Attendee/Badgedex/Badgedex.tsx index 84c29ef3..73d2b48a 100644 --- a/layout/Attendee/Badgedex/Badgedex.tsx +++ b/layout/Attendee/Badgedex/Badgedex.tsx @@ -9,27 +9,15 @@ import ErrorMessage from "@components/ErrorMessage"; import Badge from "@components/Badge"; import BadgeFilter from "@components/BadgeFilter"; import GoToTop from "@components/GoToTop"; - -export interface Badges { - avatar: string; - begin: string; - description: string; - end: string; - id: number; - name: string; - tokens: number; - type: number; -} +import { IBadge } from "@context/Auth"; interface UserWithBadges { - user: { - badges: Badges[]; - }; + badges: IBadge[]; } function Badgedex() { - const { user }: UserWithBadges = useAuth(); - const [allBadges, updateAllBadges] = useState([]); + const { user } = useAuth() as { user: UserWithBadges }; + const [allBadges, updateAllBadges] = useState([]); const [all, updateAll] = useState(true); const [filter, updateFilter] = useState(null); const [error, setError] = useState(); diff --git a/layout/Attendee/Identifier/Identifier.tsx b/layout/Attendee/Identifier/Identifier.tsx index a07114e6..4d3a3db7 100644 --- a/layout/Attendee/Identifier/Identifier.tsx +++ b/layout/Attendee/Identifier/Identifier.tsx @@ -1,10 +1,10 @@ -import { useAuth, withAuth } from "@context/Auth"; +import { IAttendee, useAuth, withAuth } from "@context/Auth"; import Layout from "@components/Layout"; import QRCodeCanvas from "@components/QRCodeCanvas"; function Identifier() { - const { user } = useAuth(); + const { user } = useAuth() as { user: IAttendee }; return ( ({ ...product, is_product: true })) diff --git a/layout/Attendee/Profile/Profile.tsx b/layout/Attendee/Profile/Profile.tsx index e02f00ee..02654b43 100644 --- a/layout/Attendee/Profile/Profile.tsx +++ b/layout/Attendee/Profile/Profile.tsx @@ -1,6 +1,6 @@ import { useState, useEffect } from "react"; -import { withAuth, useAuth } from "@context/Auth"; +import { withAuth, useAuth, IAttendee } from "@context/Auth"; import Form from "@components/Form"; import Input from "@components/Input"; @@ -22,11 +22,14 @@ interface Course { } function Profile({ courses }) { - const { user, editUser } = useAuth(); + const { user, editUser } = useAuth() as { + user: IAttendee; + editUser: (username: FormData) => void; + }; const [avatar, setAvatar] = useState(null); const [editing, setEditing] = useState(false); const [username, setUsername] = useState(user.nickname || ""); - const [course, setCourse] = useState(user.course || ""); + const [course, setCourse] = useState(user.course.toString() || ""); const [photoFileUrl, setPhotoFileUrl] = useState(user.avatar); diff --git a/layout/Attendee/Store/Store.tsx b/layout/Attendee/Store/Store.tsx index 600bd760..bb757b2f 100644 --- a/layout/Attendee/Store/Store.tsx +++ b/layout/Attendee/Store/Store.tsx @@ -1,6 +1,6 @@ import { useState, useEffect } from "react"; -import { withAuth, useAuth } from "@context/Auth"; +import { withAuth, useAuth, IAttendee } from "@context/Auth"; import * as api from "@lib/api"; @@ -11,7 +11,7 @@ import Balance from "@components/Balance"; import { Product } from "./components"; function Store() { - const { user } = useAuth(); + const { user } = useAuth() as { user: IAttendee }; const [products, setProducts] = useState(null); diff --git a/layout/Attendee/Wheel/Wheel.tsx b/layout/Attendee/Wheel/Wheel.tsx index 7cfc1e7f..9f49d12b 100644 --- a/layout/Attendee/Wheel/Wheel.tsx +++ b/layout/Attendee/Wheel/Wheel.tsx @@ -1,5 +1,5 @@ import { useState, useEffect } from "react"; -import { withAuth, useAuth } from "@context/Auth"; +import { withAuth, useAuth, IAttendee } from "@context/Auth"; import Heading from "@components/Heading"; import Button from "@components/Button"; @@ -57,7 +57,10 @@ function WheelPage() { const angleSpeed = 20; const [st, updateState] = useState(defaultState); - const { user, refetchUser } = useAuth(); + const { user, refetchUser } = useAuth() as { + user: IAttendee; + refetchUser: () => void; + }; const [prizes, updatePrizes] = useState([]); const [price, updatePrice] = useState(null); diff --git a/layout/Challenges/Challenges.tsx b/layout/Challenges/Challenges.tsx index 119ab0dd..5b56d86f 100644 --- a/layout/Challenges/Challenges.tsx +++ b/layout/Challenges/Challenges.tsx @@ -1,5 +1,3 @@ -import { withoutAuth } from "@context/Auth"; - import Navbar from "@components/Navbar"; import Footer from "@components/Footer"; @@ -18,4 +16,4 @@ function Index() { ); } -export default withoutAuth(Index); +export default Index; diff --git a/layout/FAQs/FAQs.tsx b/layout/FAQs/FAQs.tsx index 7f4c6a3d..4e266b22 100644 --- a/layout/FAQs/FAQs.tsx +++ b/layout/FAQs/FAQs.tsx @@ -1,5 +1,3 @@ -import { withoutAuth } from "@context/Auth"; - import Navbar from "@components/Navbar"; import Footer from "@components/Footer"; @@ -40,4 +38,4 @@ function Faq() { ); } -export default withoutAuth(Faq); +export default Faq; diff --git a/layout/ForgotPassword/ForgotPassword.tsx b/layout/ForgotPassword/ForgotPassword.tsx index 7b054121..a7d235dd 100644 --- a/layout/ForgotPassword/ForgotPassword.tsx +++ b/layout/ForgotPassword/ForgotPassword.tsx @@ -1,5 +1,3 @@ -import { withoutAuth } from "@context/Auth"; - import { motion as Motion } from "framer-motion"; import Card from "@components/Card"; @@ -32,4 +30,4 @@ function ForgotPassword() { ); } -export default withoutAuth(ForgotPassword); +export default ForgotPassword; diff --git a/layout/Hackathon/Hackathon.tsx b/layout/Hackathon/Hackathon.tsx index 9c77f304..ee7a8483 100644 --- a/layout/Hackathon/Hackathon.tsx +++ b/layout/Hackathon/Hackathon.tsx @@ -1,5 +1,3 @@ -import { withoutAuth } from "@context/Auth"; - import { Hero, Awards, Regulations } from "./components"; import PeopleShowcase from "@components/PeopleShowcase"; @@ -82,4 +80,4 @@ function Index() { ); } -export default withoutAuth(Index); +export default Index; diff --git a/layout/Home/Home.tsx b/layout/Home/Home.tsx index da3dd52b..36fea787 100644 --- a/layout/Home/Home.tsx +++ b/layout/Home/Home.tsx @@ -1,5 +1,3 @@ -import { withoutAuth } from "@context/Auth"; - import { Hero, Sponsors, Hackathon, Speakers, Partners } from "./components"; import Navbar from "@components/Navbar"; @@ -19,4 +17,4 @@ function Home() { ); } -export default withoutAuth(Home); +export default Home; diff --git a/layout/Login/Login.tsx b/layout/Login/Login.tsx index 015c01b7..012ef8f8 100644 --- a/layout/Login/Login.tsx +++ b/layout/Login/Login.tsx @@ -3,9 +3,10 @@ import { motion as Motion } from "framer-motion"; -import { withoutAuth } from "@context/Auth"; +import { useAuth } from "@context/Auth"; import Card from "@components/Card"; +import { Router, useRouter } from "next/router"; import Return from "@components/Return"; @@ -14,6 +15,17 @@ import Text from "@layout/moonstone/authentication/Text"; import { LoginForm } from "./components"; function Login() { + const router = useRouter(); + const { user } = useAuth(); + + if (user) { + router.replace( + (router.query.from && decodeURIComponent(router.query.from as string)) ?? + "/" + ); + return null; + } + return (
@@ -46,4 +58,4 @@ function Login() { ); } -export default withoutAuth(Login); +export default Login; diff --git a/layout/Product/Product.tsx b/layout/Product/Product.tsx index 1566b7f9..f60d0d27 100644 --- a/layout/Product/Product.tsx +++ b/layout/Product/Product.tsx @@ -10,11 +10,19 @@ import Balance from "@components/Balance"; import Button from "@components/Button"; import Layout from "@components/Layout"; +interface IUserWithBalance { + token_balance: number; + badge_count: number; +} + function ProductSlug() { // TODO: add type to product const [product, updateProduct] = useState({}); const router = useRouter(); - const { user, refetchUser } = useAuth(); + const { user, refetchUser } = useAuth() as { + user: IUserWithBalance; + refetchUser: () => void; + }; const [needsUpdate, updateProductInfo] = useState(false); diff --git a/layout/ResetPassword/ResetPassword.tsx b/layout/ResetPassword/ResetPassword.tsx index c7d63d7c..856c89ae 100644 --- a/layout/ResetPassword/ResetPassword.tsx +++ b/layout/ResetPassword/ResetPassword.tsx @@ -1,5 +1,3 @@ -import { withoutAuth } from "@context/Auth"; - import { motion as Motion } from "framer-motion"; import Card from "@components/Card"; @@ -43,4 +41,4 @@ function Reset() { ); } -export default withoutAuth(Reset); +export default Reset; diff --git a/layout/Schedule/Schedule.tsx b/layout/Schedule/Schedule.tsx index 1514902e..d5a987df 100644 --- a/layout/Schedule/Schedule.tsx +++ b/layout/Schedule/Schedule.tsx @@ -1,5 +1,3 @@ -import { withoutAuth } from "@context/Auth"; - import { Hero } from "./components"; import Navbar from "@components/Navbar"; @@ -18,4 +16,4 @@ function Index() { ); } -export default withoutAuth(Index); +export default Index; diff --git a/layout/SignUp/SignUp.tsx b/layout/SignUp/SignUp.tsx index 8418bd54..3f6638cf 100644 --- a/layout/SignUp/SignUp.tsx +++ b/layout/SignUp/SignUp.tsx @@ -1,5 +1,3 @@ -import { withoutAuth } from "@context/Auth"; - import { motion as Motion } from "framer-motion"; import Card from "@components/Card"; @@ -33,4 +31,4 @@ function Signup({ courses }) { ); } -export default withoutAuth(Signup); +export default Signup; diff --git a/layout/Speakers/Speakers.tsx b/layout/Speakers/Speakers.tsx index 9baec197..1cc034f7 100644 --- a/layout/Speakers/Speakers.tsx +++ b/layout/Speakers/Speakers.tsx @@ -1,5 +1,4 @@ import { useRouter } from "next/router"; -import { withoutAuth } from "@context/Auth"; import { Hero, Schedule } from "./components"; @@ -18,4 +17,4 @@ function Speakers() { ); } -export default withoutAuth(Speakers); +export default Speakers; diff --git a/layout/Sponsor/Scanner/Scanner.tsx b/layout/Sponsor/Scanner/Scanner.tsx index 95d4f49f..84ea82f2 100644 --- a/layout/Sponsor/Scanner/Scanner.tsx +++ b/layout/Sponsor/Scanner/Scanner.tsx @@ -1,12 +1,12 @@ import { useState, useRef, useEffect } from "react"; import { giveBadge } from "@lib/api"; -import { withAuth, useAuth } from "@context/Auth"; +import { withAuth, useAuth, ISponsor } from "@context/Auth"; import QRScanner, { FEEDBACK } from "@components/QRScanner"; import Layout from "@components/Layout"; const SponsorBadges: React.FC = () => { - const { user } = useAuth(); + const { user } = useAuth() as { user: ISponsor }; const pauseRef = useRef(false); const [feedback, setFeedback] = useState( FEEDBACK.SCANNING diff --git a/layout/Staff/Prizes/Prizes.tsx b/layout/Staff/Prizes/Prizes.tsx index 4c9e76e4..3a2a4e90 100644 --- a/layout/Staff/Prizes/Prizes.tsx +++ b/layout/Staff/Prizes/Prizes.tsx @@ -1,13 +1,13 @@ import { useState, useRef } from "react"; import { useRouter } from "next/router"; -import { withAuth, useAuth } from "@context/Auth"; +import { withAuth, useAuth, IStaff } from "@context/Auth"; import Layout from "@components/Layout"; import QRScanner, { FEEDBACK } from "@components/QRScanner"; function Prizes() { - const { user } = useAuth(); + const { user } = useAuth() as { user: IStaff }; const router = useRouter(); const pauseRef = useRef(false); const [feedback, setFeedback] = useState(FEEDBACK.SCANNING); @@ -23,7 +23,7 @@ function Prizes() {