├── .eslintignore ├── .eslintrc.json ├── .github └── workflows │ ├── pull-request.yaml │ └── sync-template.yaml ├── .gitignore ├── .husky └── pre-push ├── .prettierignore ├── .prettierrc ├── README.md ├── auth.d.ts ├── components.json ├── next.config.mjs ├── package-lock.json ├── package.json ├── postcss.config.js ├── public └── engineering │ └── hackathon │ ├── judgeAssets1.webp │ ├── judgeAssets2.webp │ ├── judgeAssets3.webp │ └── judgeAssets4.webp ├── src ├── app │ ├── admin │ │ ├── dashboard │ │ │ ├── admins │ │ │ │ └── page.tsx │ │ │ ├── committees │ │ │ │ └── page.tsx │ │ │ ├── feedback │ │ │ │ └── page.tsx │ │ │ ├── interests │ │ │ │ └── page.tsx │ │ │ ├── judges │ │ │ │ └── page.tsx │ │ │ ├── leads │ │ │ │ └── page.tsx │ │ │ ├── mentors │ │ │ │ └── page.tsx │ │ │ ├── panels │ │ │ │ └── page.tsx │ │ │ ├── participants │ │ │ │ └── page.tsx │ │ │ ├── resumes │ │ │ │ └── page.tsx │ │ │ ├── sponsors │ │ │ │ └── page.tsx │ │ │ ├── teams │ │ │ │ └── page.tsx │ │ │ └── volunteers │ │ │ │ └── page.tsx │ │ ├── layout.tsx │ │ └── services │ │ │ ├── calendar │ │ │ └── page.tsx │ │ │ ├── checkin │ │ │ └── page.tsx │ │ │ ├── contacts │ │ │ └── page.tsx │ │ │ ├── judging │ │ │ └── page.tsx │ │ │ ├── notes │ │ │ └── page.tsx │ │ │ ├── settings │ │ │ └── page.tsx │ │ │ ├── statistics │ │ │ └── page.tsx │ │ │ └── timer │ │ │ └── page.tsx │ ├── api │ │ ├── auth │ │ │ └── [...nextauth] │ │ │ │ └── route.ts │ │ ├── checkin │ │ │ └── route.js │ │ ├── contacts │ │ │ └── route.ts │ │ ├── dashboard │ │ │ ├── [type] │ │ │ │ └── route.js │ │ │ ├── feedback │ │ │ │ └── route.js │ │ │ ├── ideas │ │ │ │ └── route.ts │ │ │ ├── resumes │ │ │ │ └── route.js │ │ │ └── teams │ │ │ │ └── route.js │ │ ├── judging │ │ │ ├── route.js │ │ │ └── start │ │ │ │ └── route.js │ │ ├── members │ │ │ └── route.js │ │ ├── notes │ │ │ └── route.js │ │ ├── participant │ │ │ └── route.ts │ │ ├── settings │ │ │ └── route.js │ │ ├── statistics │ │ │ └── route.ts │ │ ├── team │ │ │ └── route.js │ │ └── teams │ │ │ └── ideas │ │ │ └── route.ts │ ├── apply │ │ ├── admin │ │ │ └── page.tsx │ │ ├── committee │ │ │ └── page.tsx │ │ ├── feedback │ │ │ └── page.tsx │ │ ├── idea │ │ │ └── page.tsx │ │ ├── interest │ │ │ └── page.tsx │ │ ├── layout.tsx │ │ ├── lead │ │ │ └── page.tsx │ │ ├── mentor │ │ │ └── page.tsx │ │ ├── panel │ │ │ └── page.tsx │ │ ├── participant │ │ │ └── page.tsx │ │ ├── sponsor │ │ │ └── page.tsx │ │ └── volunteer │ │ │ └── page.tsx │ ├── auth │ │ └── error │ │ │ └── page.tsx │ ├── error.tsx │ ├── favicon.ico │ ├── globals.css │ ├── judge │ │ ├── end │ │ │ └── page.tsx │ │ ├── layout.tsx │ │ ├── page.tsx │ │ ├── register │ │ │ └── page.tsx │ │ ├── start │ │ │ ├── [id] │ │ │ │ └── page.tsx │ │ │ └── page.tsx │ │ └── structure │ │ │ └── page.tsx │ ├── layout.tsx │ ├── loading.tsx │ ├── manifest.tsx │ ├── not-found.tsx │ ├── page.tsx │ ├── resources │ │ └── page.tsx │ ├── robots.txt │ ├── sitemap.ts │ ├── user │ │ ├── checkin │ │ │ └── page.tsx │ │ ├── dashboard │ │ │ └── page.tsx │ │ ├── find │ │ │ └── page.tsx │ │ ├── join │ │ │ └── [team] │ │ │ │ └── page.tsx │ │ ├── layout.tsx │ │ ├── schedule │ │ │ └── page.tsx │ │ └── team │ │ │ └── page.tsx │ └── visualize │ │ └── page.tsx ├── components │ ├── admin │ │ ├── dashboards │ │ │ ├── admins.tsx │ │ │ ├── committees.tsx │ │ │ ├── dashboard │ │ │ │ ├── dashboard.jsx │ │ │ │ ├── filters.jsx │ │ │ │ ├── table.jsx │ │ │ │ ├── toolbar.jsx │ │ │ │ └── view.jsx │ │ │ ├── feedback.tsx │ │ │ ├── interests.tsx │ │ │ ├── judges.tsx │ │ │ ├── leads.tsx │ │ │ ├── mentors.tsx │ │ │ ├── panels.tsx │ │ │ ├── participants.tsx │ │ │ ├── resumes.tsx │ │ │ ├── sponsors.tsx │ │ │ ├── teams.tsx │ │ │ └── volunteers.tsx │ │ └── services │ │ │ ├── calendar │ │ │ ├── actions.ts │ │ │ ├── calendar.tsx │ │ │ ├── event.tsx │ │ │ ├── index.tsx │ │ │ ├── modal.tsx │ │ │ └── toolbar.tsx │ │ │ ├── checkin │ │ │ ├── actions.ts │ │ │ ├── checkIn.tsx │ │ │ ├── index.tsx │ │ │ └── scanner.tsx │ │ │ ├── contacts │ │ │ ├── contact.jsx │ │ │ └── index.tsx │ │ │ ├── judging │ │ │ ├── judging.jsx │ │ │ ├── table.jsx │ │ │ └── toolbar.jsx │ │ │ ├── notes │ │ │ ├── actions.ts │ │ │ ├── index.tsx │ │ │ ├── notes.tsx │ │ │ ├── rounds.tsx │ │ │ └── teams.tsx │ │ │ ├── settings │ │ │ └── index.tsx │ │ │ ├── statistics │ │ │ ├── heatmap.tsx │ │ │ └── index.jsx │ │ │ ├── timer │ │ │ ├── clock.jsx │ │ │ └── index.tsx │ │ │ └── upload.jsx │ ├── checkbox.tsx │ ├── email │ │ ├── acceptance.tsx │ │ ├── confirmation.tsx │ │ ├── index.tsx │ │ ├── rejection.tsx │ │ └── template.tsx │ ├── error.tsx │ ├── form │ │ ├── admin.tsx │ │ ├── committee.tsx │ │ ├── feedback.tsx │ │ ├── form │ │ │ ├── confirmation.tsx │ │ │ ├── index.jsx │ │ │ ├── questions.jsx │ │ │ ├── status.jsx │ │ │ ├── terms.tsx │ │ │ └── upload.jsx │ │ ├── ideas.tsx │ │ ├── interest.tsx │ │ ├── judge.tsx │ │ ├── lead.tsx │ │ ├── mentor.tsx │ │ ├── panel.tsx │ │ ├── participant.tsx │ │ ├── sponsor.tsx │ │ └── volunteer.tsx │ ├── judging │ │ └── start │ │ │ ├── confirm.tsx │ │ │ ├── dashboard.tsx │ │ │ ├── form.jsx │ │ │ ├── index.tsx │ │ │ └── questions.jsx │ ├── live │ │ ├── about.tsx │ │ ├── committees.tsx │ │ ├── faq.tsx │ │ ├── footer.tsx │ │ ├── index.tsx │ │ ├── judges.tsx │ │ ├── landing.tsx │ │ ├── schedule │ │ │ ├── events.tsx │ │ │ └── index.tsx │ │ ├── sponsors.tsx │ │ ├── team.tsx │ │ └── tracks.tsx │ ├── loading.tsx │ ├── navigation.tsx │ ├── protected.tsx │ ├── providers.tsx │ ├── release.tsx │ ├── resources │ │ ├── hackpack.tsx │ │ └── resources.tsx │ ├── select.jsx │ ├── ui │ │ ├── accordion.tsx │ │ ├── alert-dialog.tsx │ │ ├── badge.tsx │ │ ├── button.tsx │ │ ├── card.tsx │ │ ├── chart.tsx │ │ ├── checkbox.tsx │ │ ├── collapsible.tsx │ │ ├── countdown.tsx │ │ ├── dialog.tsx │ │ ├── dropdown-menu.tsx │ │ ├── input-otp.tsx │ │ ├── input.tsx │ │ ├── label.tsx │ │ ├── progress.tsx │ │ ├── radio-group.tsx │ │ ├── separator.tsx │ │ ├── sheet.tsx │ │ ├── sidebar.tsx │ │ ├── skeleton.tsx │ │ ├── slider.tsx │ │ ├── table.tsx │ │ ├── tabs.tsx │ │ ├── textarea.tsx │ │ ├── title.tsx │ │ ├── toggle-group.tsx │ │ ├── toggle.tsx │ │ └── tooltip.tsx │ └── user │ │ ├── actions │ │ └── fetchTeam.ts │ │ ├── checkIn.tsx │ │ ├── dashboard │ │ ├── bulletlist.tsx │ │ ├── countdown.tsx │ │ ├── index.tsx │ │ ├── packing.tsx │ │ ├── rooms.tsx │ │ └── tile.tsx │ │ ├── find │ │ ├── idea.tsx │ │ └── index.tsx │ │ ├── header.tsx │ │ ├── join │ │ ├── index.tsx │ │ └── invite.tsx │ │ ├── schedule │ │ ├── Event.tsx │ │ ├── Schedule.tsx │ │ ├── Toolbar.tsx │ │ └── Wrapper.tsx │ │ ├── team │ │ ├── details.jsx │ │ ├── new.jsx │ │ └── team.tsx │ │ └── toolbar.tsx ├── data │ ├── admin │ │ ├── admins.tsx │ │ ├── calendar.ts │ │ ├── columns.tsx │ │ ├── committees.tsx │ │ ├── dashboard.ts │ │ ├── feedback.tsx │ │ ├── icons.tsx │ │ ├── interests.tsx │ │ ├── judges.tsx │ │ ├── leads.tsx │ │ ├── mentors.tsx │ │ ├── panelists.tsx │ │ ├── participants.tsx │ │ ├── resumes.tsx │ │ ├── sponsors.tsx │ │ ├── statistics.ts │ │ ├── teams.tsx │ │ └── volunteers.tsx │ ├── button.js │ ├── bytes.ts │ ├── config.ts │ ├── faq.ts │ ├── form │ │ ├── admins.ts │ │ ├── committees.ts │ │ ├── countries.ts │ │ ├── feedback.ts │ │ ├── ideas.ts │ │ ├── information.ts │ │ ├── interest.ts │ │ ├── judge.ts │ │ ├── leads.ts │ │ ├── mentors.ts │ │ ├── panelists.ts │ │ ├── participant.ts │ │ ├── schools.ts │ │ ├── sponsors.ts │ │ └── volunteers.ts │ ├── judge │ │ ├── form.ts │ │ └── judge.ts │ ├── navigation.tsx │ ├── releases.ts │ ├── statuses.ts │ ├── tags.ts │ └── user │ │ ├── hackpacks.tsx │ │ ├── judging.ts │ │ ├── members.ts │ │ ├── participant.tsx │ │ ├── rules.ts │ │ └── team.ts ├── hooks │ └── use-mobile.tsx ├── middleware.ts ├── schemas │ ├── admin.ts │ ├── committee.ts │ ├── feedback.ts │ ├── idea.ts │ ├── interest.ts │ ├── judge.ts │ ├── judging.ts │ ├── lead.ts │ ├── mentor.ts │ ├── panel.ts │ ├── participant.ts │ ├── sponsor.ts │ └── volunteer.ts ├── types │ ├── auth.ts │ ├── calendar.ts │ ├── dashboard.ts │ ├── forms.ts │ ├── rounds.ts │ └── users.ts └── utils │ ├── api.ts │ ├── auth.ts │ ├── convert.ts │ ├── download.ts │ ├── email.ts │ ├── error.ts │ ├── firebase.ts │ ├── form.ts │ ├── react-query.tsx │ ├── signin.ts │ ├── tailwind.ts │ └── toaster.ts ├── tailwind.config.js └── tsconfig.json /.eslintignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # next.js 12 | /.next/ 13 | /out/ 14 | 15 | # production 16 | /build 17 | 18 | # misc 19 | .DS_Store 20 | *.pem 21 | 22 | # debug 23 | npm-debug.log* 24 | yarn-debug.log* 25 | yarn-error.log* 26 | 27 | # local env files 28 | .env*.local 29 | 30 | # vercel 31 | .vercel 32 | 33 | # typescript 34 | *.tsbuildinfo 35 | next-env.d.ts 36 | 37 | .env 38 | 39 | # cypress 40 | /cypress 41 | cypress.config.js 42 | 43 | # tailwind 44 | tailwind.config.js -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "es2021": true 5 | }, 6 | "extends": [ 7 | "next/core-web-vitals", 8 | "google", 9 | "prettier", 10 | "plugin:@tanstack/eslint-plugin-query/recommended", 11 | "plugin:@typescript-eslint/recommended" 12 | ], 13 | "overrides": [], 14 | "parserOptions": { 15 | "ecmaVersion": "latest", 16 | "sourceType": "module" 17 | }, 18 | "plugins": ["react", "@typescript-eslint"], 19 | "rules": { 20 | "require-jsdoc": 0, 21 | "jsx-a11y/alt-text": 1, 22 | "no-unused-vars": "off", 23 | "@typescript-eslint/no-unused-vars": [ 24 | "error", 25 | { 26 | "vars": "all", 27 | "varsIgnorePattern": "^_", 28 | "args": "after-used", 29 | "argsIgnorePattern": "^_" 30 | } 31 | ] 32 | }, 33 | "settings": { 34 | "react": { 35 | "version": "detect" 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /.github/workflows/pull-request.yaml: -------------------------------------------------------------------------------- 1 | name: Hackathon Website 2 | 3 | on: 4 | pull_request: 5 | branches: [main, dev] 6 | workflow_dispatch: 7 | 8 | jobs: 9 | format: 10 | name: formatting 11 | runs-on: ubuntu-latest 12 | 13 | steps: 14 | - uses: actions/checkout@v4 15 | - name: Use ACM Formatting 16 | uses: acm-ucr/formatting@v2 17 | 18 | lint: 19 | name: linting 20 | runs-on: ubuntu-latest 21 | 22 | steps: 23 | - uses: actions/checkout@v4 24 | - name: Use ACM Linting 25 | uses: acm-ucr/linting@v2 26 | 27 | images: 28 | runs-on: ubuntu-latest 29 | steps: 30 | - uses: actions/checkout@v4 31 | - name: Use ACM Images Optimization 32 | uses: acm-ucr/images@v2 33 | with: 34 | branch: ${{ github.head_ref }} 35 | env: 36 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 37 | 38 | build: 39 | name: build 40 | runs-on: ubuntu-latest 41 | 42 | steps: 43 | - uses: actions/checkout@v4 44 | - name: Use ACM Build 45 | uses: acm-ucr/build@v2 46 | 47 | env: 48 | NEXT_PUBLIC_FIREBASE_API_KEY: ${{secrets.NEXT_PUBLIC_FIREBASE_API_KEY}} 49 | NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN: ${{secrets.NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN}} 50 | NEXT_PUBLIC_FIREBASE_PROJECT_ID: ${{secrets.NEXT_PUBLIC_FIREBASE_PROJECT_ID}} 51 | NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET: ${{secrets.NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET}} 52 | NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID: ${{secrets.NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID}} 53 | NEXT_PUBLIC_FIREBASE_APP_ID: ${{secrets.NEXT_PUBLIC_FIREBASE_APP_ID}} 54 | NEXT_PUBLIC_FIREBASE_CLIENT_EMAIL: ${{secrets.NEXT_PUBLIC_FIREBASE_CLIENT_EMAIL}} 55 | NEXT_PUBLIC_FIREBASE_PRIVATE_KEY: ${{secrets.NEXT_PUBLIC_FIREBASE_PRIVATE_KEY}} 56 | NEXT_PUBLIC_GOOGLE_CLIENT_ID: ${{secrets.NEXT_PUBLIC_GOOGLE_CLIENT_ID}} 57 | NEXT_PUBLIC_GOOGLE_CLIENT_SECRET: ${{secrets.NEXT_PUBLIC_GOOGLE_CLIENT_SECRET}} 58 | NEXTAUTH_SECRET: ${{secrets.NEXTAUTH_SECRET}} 59 | NEXTAUTH_URL: ${{secrets.NEXTAUTH_URL}} 60 | RESEND_API_KEY: "re_test_api_key" 61 | -------------------------------------------------------------------------------- /.github/workflows/sync-template.yaml: -------------------------------------------------------------------------------- 1 | name: Sync Changes from Template 2 | on: 3 | schedule: 4 | # Run at 6:00 AM every day 5 | - cron: "0 14 * * *" 6 | 7 | jobs: 8 | sync-from-template: 9 | # don't run this job on the template 10 | if: github.repository != 'acm-ucr/hackathon-website' 11 | name: Sync changes from acm-ucr/hackathon-website 12 | runs-on: ubuntu-latest 13 | 14 | steps: 15 | - name: Checkout Current Repo 16 | uses: actions/checkout@v4 17 | with: 18 | path: ${{ github.repository }} 19 | token: ${{ secrets.WORKFLOW_TOKEN }} 20 | 21 | - name: Fetch all 22 | working-directory: ${{ github.repository }} 23 | run: | 24 | git remote add template https://github.com/acm-ucr/hackathon-website 25 | git fetch --all 26 | 27 | - name: Checkout and push Template Dev Branch 28 | working-directory: ${{ github.repository }} 29 | run: | 30 | branch_exists=$(git branch --list --remote | { grep -F "origin/sync-template" || true; } ) 31 | if [ -z "$branch_exists" ] 32 | then 33 | git checkout -b sync-template template/dev 34 | git push origin sync-template 35 | gh pr create --base dev --head sync-template --title "Sync Template Changes" --body "Syncing template changes" 36 | else 37 | git checkout -b sync-template origin/sync-template 38 | git pull template dev 39 | git push 40 | fi 41 | env: 42 | GITHUB_TOKEN: ${{ secrets.WORKFLOW_TOKEN }} 43 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # jetbrains 4 | .idea/ 5 | 6 | # dependencies 7 | /node_modules 8 | /.pnp 9 | .pnp.js 10 | 11 | # testing 12 | /coverage 13 | 14 | # next.js 15 | /.next/ 16 | /out/ 17 | 18 | # production 19 | /build 20 | 21 | # misc 22 | .DS_Store 23 | *.pem 24 | 25 | # debug 26 | npm-debug.log* 27 | yarn-debug.log* 28 | yarn-error.log* 29 | 30 | # local env files 31 | .env*.local 32 | 33 | # vercel 34 | .vercel 35 | 36 | # typescript 37 | *.tsbuildinfo 38 | next-env.d.ts 39 | 40 | .env 41 | -------------------------------------------------------------------------------- /.husky/pre-push: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | . "$(dirname -- "$0")/_/husky.sh" 3 | 4 | npm run check && npm run eslint 5 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # next.js 12 | /.next/ 13 | /out/ 14 | 15 | # production 16 | /build 17 | 18 | # misc 19 | .DS_Store 20 | *.pem 21 | 22 | # debug 23 | npm-debug.log* 24 | yarn-debug.log* 25 | yarn-error.log* 26 | 27 | # local env files 28 | .env*.local 29 | 30 | # vercel 31 | .vercel 32 | 33 | # typescript 34 | *.tsbuildinfo 35 | next-env.d.ts 36 | 37 | .env 38 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": ["prettier-plugin-tailwindcss"] 3 | } 4 | -------------------------------------------------------------------------------- /auth.d.ts: -------------------------------------------------------------------------------- 1 | import "next-auth"; 2 | 3 | declare module "next-auth" { 4 | export interface User { 5 | id: string; 6 | email: string; 7 | firstName: string; 8 | lastName: string; 9 | image: string; 10 | roles: { 11 | [key: string]: number; 12 | }; 13 | team: string; 14 | photo: string; 15 | } 16 | 17 | export interface Session { 18 | user: User; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /components.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://ui.shadcn.com/schema.json", 3 | "style": "default", 4 | "rsc": true, 5 | "tsx": true, 6 | "tailwind": { 7 | "config": "tailwind.config.js", 8 | "css": "src/app/globals.css", 9 | "baseColor": "slate", 10 | "cssVariables": false, 11 | "prefix": "" 12 | }, 13 | "aliases": { 14 | "components": "@/components", 15 | "utils": "@/utils/tailwind" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /next.config.mjs: -------------------------------------------------------------------------------- 1 | import nextBundleAnalyzer from "@next/bundle-analyzer"; 2 | 3 | const withBundleAnalyzer = nextBundleAnalyzer({ 4 | enabled: process.env.ANALYZE === "true", 5 | }); 6 | 7 | const nextConfig = { 8 | async redirects() { 9 | return [ 10 | { 11 | source: "/admin", 12 | destination: "/admin/dashboard/participants", 13 | permanent: true, 14 | }, 15 | { 16 | source: "/admin/dashboard", 17 | destination: "/admin/dashboard/participants", 18 | permanent: true, 19 | }, 20 | 21 | { 22 | source: "/user", 23 | destination: "/user/dashboard", 24 | permanent: true, 25 | }, 26 | { 27 | source: "/apply", 28 | destination: "/apply/participant", 29 | permanent: true, 30 | }, 31 | ]; 32 | }, 33 | images: { 34 | remotePatterns: [ 35 | { 36 | protocol: "https", 37 | hostname: "lh3.googleusercontent.com", 38 | port: "", 39 | pathname: "/a/**", 40 | }, 41 | ], 42 | }, 43 | }; 44 | 45 | export default withBundleAnalyzer(nextConfig); 46 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | }; 7 | -------------------------------------------------------------------------------- /public/engineering/hackathon/judgeAssets1.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/acm-ucr/aurora/e2d09fdfbfe93b499a17d2f3afaee64e9edcc525/public/engineering/hackathon/judgeAssets1.webp -------------------------------------------------------------------------------- /public/engineering/hackathon/judgeAssets2.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/acm-ucr/aurora/e2d09fdfbfe93b499a17d2f3afaee64e9edcc525/public/engineering/hackathon/judgeAssets2.webp -------------------------------------------------------------------------------- /public/engineering/hackathon/judgeAssets3.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/acm-ucr/aurora/e2d09fdfbfe93b499a17d2f3afaee64e9edcc525/public/engineering/hackathon/judgeAssets3.webp -------------------------------------------------------------------------------- /public/engineering/hackathon/judgeAssets4.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/acm-ucr/aurora/e2d09fdfbfe93b499a17d2f3afaee64e9edcc525/public/engineering/hackathon/judgeAssets4.webp -------------------------------------------------------------------------------- /src/app/admin/dashboard/admins/page.tsx: -------------------------------------------------------------------------------- 1 | import Admins from "@/components/admin/dashboards/admins"; 2 | import { SearchParams } from "@/types/dashboard"; 3 | 4 | export const metadata = { 5 | title: "Admin | Admins", 6 | }; 7 | 8 | const Page = ({ searchParams }: { searchParams: SearchParams }) => { 9 | return ; 10 | }; 11 | 12 | export default Page; 13 | -------------------------------------------------------------------------------- /src/app/admin/dashboard/committees/page.tsx: -------------------------------------------------------------------------------- 1 | import Committees from "@/components/admin/dashboards/committees"; 2 | import { SearchParams } from "@/types/dashboard"; 3 | 4 | export const metadata = { 5 | title: "Admin | Committees", 6 | }; 7 | 8 | const Page = ({ searchParams }: { searchParams: SearchParams }) => { 9 | return ; 10 | }; 11 | 12 | export default Page; 13 | -------------------------------------------------------------------------------- /src/app/admin/dashboard/feedback/page.tsx: -------------------------------------------------------------------------------- 1 | import Feedback from "@/components/admin/dashboards/feedback"; 2 | import { SearchParams } from "@/types/dashboard"; 3 | 4 | export const metadata = { 5 | title: "Admin | Feedback", 6 | }; 7 | 8 | const Page = ({ searchParams }: { searchParams: SearchParams }) => { 9 | return ; 10 | }; 11 | 12 | export default Page; 13 | -------------------------------------------------------------------------------- /src/app/admin/dashboard/interests/page.tsx: -------------------------------------------------------------------------------- 1 | import Interests from "@/components/admin/dashboards/interests"; 2 | import { SearchParams } from "@/types/dashboard"; 3 | 4 | export const metadata = { 5 | title: "Admin | Interests", 6 | }; 7 | 8 | const Page = ({ searchParams }: { searchParams: SearchParams }) => { 9 | return ; 10 | }; 11 | 12 | export default Page; 13 | -------------------------------------------------------------------------------- /src/app/admin/dashboard/judges/page.tsx: -------------------------------------------------------------------------------- 1 | import Judges from "@/components/admin/dashboards/judges"; 2 | import { SearchParams } from "@/types/dashboard"; 3 | 4 | export const metadata = { 5 | title: "Admin | Judges", 6 | }; 7 | 8 | const Page = ({ searchParams }: { searchParams: SearchParams }) => { 9 | return ; 10 | }; 11 | 12 | export default Page; 13 | -------------------------------------------------------------------------------- /src/app/admin/dashboard/leads/page.tsx: -------------------------------------------------------------------------------- 1 | import Leads from "@/components/admin/dashboards/leads"; 2 | import { SearchParams } from "@/types/dashboard"; 3 | 4 | export const metadata = { 5 | title: "Admin | Leads", 6 | }; 7 | 8 | const Page = ({ searchParams }: { searchParams: SearchParams }) => { 9 | return ; 10 | }; 11 | 12 | export default Page; 13 | -------------------------------------------------------------------------------- /src/app/admin/dashboard/mentors/page.tsx: -------------------------------------------------------------------------------- 1 | import Mentors from "@/components/admin/dashboards/mentors"; 2 | import { SearchParams } from "@/types/dashboard"; 3 | 4 | export const metadata = { 5 | title: "Admin | Mentors", 6 | }; 7 | 8 | const Page = ({ searchParams }: { searchParams: SearchParams }) => { 9 | return ; 10 | }; 11 | 12 | export default Page; 13 | -------------------------------------------------------------------------------- /src/app/admin/dashboard/panels/page.tsx: -------------------------------------------------------------------------------- 1 | import Panels from "@/components/admin/dashboards/panels"; 2 | import { SearchParams } from "@/types/dashboard"; 3 | 4 | export const metadata = { 5 | title: "Admin | Panels", 6 | }; 7 | 8 | const Page = ({ searchParams }: { searchParams: SearchParams }) => { 9 | return ; 10 | }; 11 | 12 | export default Page; 13 | -------------------------------------------------------------------------------- /src/app/admin/dashboard/participants/page.tsx: -------------------------------------------------------------------------------- 1 | import Participants from "@/components/admin/dashboards/participants"; 2 | import { SearchParams } from "@/types/dashboard"; 3 | 4 | export const metadata = { 5 | title: "Admin | Participants", 6 | }; 7 | 8 | const Page = ({ searchParams }: { searchParams: SearchParams }) => { 9 | return ; 10 | }; 11 | 12 | export default Page; 13 | -------------------------------------------------------------------------------- /src/app/admin/dashboard/resumes/page.tsx: -------------------------------------------------------------------------------- 1 | import Resumes from "@/components/admin/dashboards/resumes"; 2 | import { SearchParams } from "@/types/dashboard"; 3 | 4 | export const metadata = { 5 | title: "Admin | Resumes", 6 | }; 7 | 8 | const Page = ({ searchParams }: { searchParams: SearchParams }) => { 9 | return ; 10 | }; 11 | 12 | export default Page; 13 | -------------------------------------------------------------------------------- /src/app/admin/dashboard/sponsors/page.tsx: -------------------------------------------------------------------------------- 1 | import Sponsors from "@/components/admin/dashboards/sponsors"; 2 | import { SearchParams } from "@/types/dashboard"; 3 | 4 | export const metadata = { 5 | title: "Admin | Sponsors", 6 | }; 7 | 8 | const Page = ({ searchParams }: { searchParams: SearchParams }) => { 9 | return ; 10 | }; 11 | 12 | export default Page; 13 | -------------------------------------------------------------------------------- /src/app/admin/dashboard/teams/page.tsx: -------------------------------------------------------------------------------- 1 | import Teams from "@/components/admin/dashboards/teams"; 2 | import { SearchParams } from "@/types/dashboard"; 3 | 4 | export const metadata = { 5 | title: "Admin | Teams", 6 | }; 7 | 8 | const Page = ({ searchParams }: { searchParams: SearchParams }) => { 9 | return ; 10 | }; 11 | 12 | export default Page; 13 | -------------------------------------------------------------------------------- /src/app/admin/dashboard/volunteers/page.tsx: -------------------------------------------------------------------------------- 1 | import Volunteers from "@/components/admin/dashboards/volunteers"; 2 | import { SearchParams } from "@/types/dashboard"; 3 | 4 | export const metadata = { 5 | title: "Admin | Volunteers", 6 | }; 7 | 8 | const Page = ({ searchParams }: { searchParams: SearchParams }) => { 9 | return ; 10 | }; 11 | 12 | export default Page; 13 | -------------------------------------------------------------------------------- /src/app/admin/layout.tsx: -------------------------------------------------------------------------------- 1 | import ProtectedPage from "@/components/protected"; 2 | import Providers from "@/components/providers"; 3 | import { Toaster } from "react-hot-toast"; 4 | import { getSession } from "@/utils/auth"; 5 | 6 | type Props = { 7 | children: React.ReactNode; 8 | }; 9 | 10 | const AdminLayout = async ({ children }: Props) => { 11 | const session = await getSession(); 12 | 13 | return ( 14 | 15 | 16 | 17 | {children} 18 | 19 | 20 | ); 21 | }; 22 | 23 | export default AdminLayout; 24 | -------------------------------------------------------------------------------- /src/app/admin/services/calendar/page.tsx: -------------------------------------------------------------------------------- 1 | import Events from "@/components/admin/services/calendar"; 2 | 3 | export const metadata = { 4 | title: "Admin | Calendar", 5 | }; 6 | 7 | export default Events; 8 | -------------------------------------------------------------------------------- /src/app/admin/services/checkin/page.tsx: -------------------------------------------------------------------------------- 1 | import CheckIn from "@/components/admin/services/checkin"; 2 | 3 | export const metadata = { 4 | title: "Admin | Check In", 5 | }; 6 | 7 | export default CheckIn; 8 | -------------------------------------------------------------------------------- /src/app/admin/services/contacts/page.tsx: -------------------------------------------------------------------------------- 1 | import Contacts from "@/components/admin/services/contacts"; 2 | 3 | export const metadata = { 4 | title: "Admin | Contacts", 5 | }; 6 | 7 | export default Contacts; 8 | -------------------------------------------------------------------------------- /src/app/admin/services/judging/page.tsx: -------------------------------------------------------------------------------- 1 | import Judging from "@/components/admin/services/judging/judging"; 2 | 3 | export const metadata = { 4 | title: "Admin | Judging", 5 | }; 6 | 7 | export default Judging; 8 | -------------------------------------------------------------------------------- /src/app/admin/services/notes/page.tsx: -------------------------------------------------------------------------------- 1 | import Notes from "@/components/admin/services/notes"; 2 | 3 | export const metadata = { 4 | title: "Admin | Notes", 5 | }; 6 | 7 | export default Notes; 8 | -------------------------------------------------------------------------------- /src/app/admin/services/settings/page.tsx: -------------------------------------------------------------------------------- 1 | import Settings from "@/components/admin/services/settings"; 2 | 3 | export const metadata = { 4 | title: "Admin | Settings", 5 | }; 6 | 7 | export default Settings; 8 | -------------------------------------------------------------------------------- /src/app/admin/services/statistics/page.tsx: -------------------------------------------------------------------------------- 1 | import Statistics from "@/components/admin/services/statistics"; 2 | 3 | export const metadata = { 4 | title: "Admin | Statistics", 5 | }; 6 | 7 | export default Statistics; 8 | -------------------------------------------------------------------------------- /src/app/admin/services/timer/page.tsx: -------------------------------------------------------------------------------- 1 | import Timer from "@/components/admin/services/timer"; 2 | 3 | export const metadata = { 4 | title: "Admin | Timer", 5 | }; 6 | 7 | export default Timer; 8 | -------------------------------------------------------------------------------- /src/app/api/auth/[...nextauth]/route.ts: -------------------------------------------------------------------------------- 1 | import NextAuth from "next-auth"; 2 | import { options } from "@/utils/auth"; 3 | 4 | // eslint-disable-next-line new-cap 5 | const handler = NextAuth(options); 6 | export { handler as GET, handler as POST }; 7 | -------------------------------------------------------------------------------- /src/app/api/checkin/route.js: -------------------------------------------------------------------------------- 1 | import { NextResponse } from "next/server"; 2 | import { db } from "@/utils/firebase"; 3 | import { 4 | doc, 5 | getDoc, 6 | updateDoc, 7 | arrayUnion, 8 | increment, 9 | setDoc, 10 | } from "firebase/firestore"; 11 | import { authenticate } from "@/utils/auth"; 12 | export const GET = async (req) => { 13 | const res = NextResponse; 14 | const { auth, message } = await authenticate({ 15 | admins: [1], 16 | }); 17 | 18 | if (auth !== 200) { 19 | return res.json( 20 | { message: `Authentication Error: ${message}` }, 21 | { status: auth }, 22 | ); 23 | } 24 | 25 | const uid = req.nextUrl.searchParams.get("uid"); 26 | 27 | try { 28 | const docSnap = await getDoc(doc(db, "users", uid)); 29 | const data = docSnap.data().events || []; 30 | return res.json({ message: "OK", items: data }, { status: 200 }); 31 | } catch (err) { 32 | return res.json( 33 | { message: `Internal Server Error: ${err}` }, 34 | { status: 500 }, 35 | ); 36 | } 37 | }; 38 | export const PUT = async (req) => { 39 | const res = NextResponse; 40 | const { auth, message } = await authenticate({ 41 | admins: [1], 42 | }); 43 | 44 | if (auth !== 200) { 45 | return res.json( 46 | { message: `Authentication Error: ${message}` }, 47 | { status: auth }, 48 | ); 49 | } 50 | 51 | const { uid, event, name } = await req.json(); 52 | 53 | try { 54 | const [_, data] = await Promise.all([ 55 | updateDoc(doc(db, "users", uid), { 56 | events: arrayUnion(event), 57 | }), 58 | getDoc(doc(db, "events", event)), 59 | ]); 60 | 61 | if (data.exists()) { 62 | await updateDoc(doc(db, "events", event), { 63 | attendance: increment(1), 64 | }); 65 | } else { 66 | await setDoc(doc(db, "events", event), { 67 | attendance: 1, 68 | name: name, 69 | }); 70 | } 71 | 72 | return res.json({ message: "OK" }, { status: 200 }); 73 | } catch (err) { 74 | return res.json( 75 | { message: `Internal Server Error: ${err}` }, 76 | { status: 500 }, 77 | ); 78 | } 79 | }; 80 | -------------------------------------------------------------------------------- /src/app/api/contacts/route.ts: -------------------------------------------------------------------------------- 1 | import { authenticate } from "@/utils/auth"; 2 | import { db } from "@/utils/firebase"; 3 | import { collection, getDocs, query, where } from "firebase/firestore"; 4 | import { NextRequest, NextResponse } from "next/server"; 5 | 6 | type contact = { 7 | id: string; 8 | name: string; 9 | email: string; 10 | phone: string; 11 | }; 12 | 13 | export const GET = async (req: NextRequest) => { 14 | const res = NextResponse; 15 | 16 | const { auth, message } = await authenticate({ 17 | admins: [1], 18 | }); 19 | 20 | if (auth !== 200) { 21 | return res.json( 22 | { message: `Authentication Error: ${message}` }, 23 | { status: auth }, 24 | ); 25 | } 26 | 27 | const role = req.nextUrl.searchParams.get("role"); 28 | const status = parseInt(req.nextUrl.searchParams.get("status")!); 29 | 30 | const output: contact[] = []; 31 | 32 | try { 33 | const snapshot = await getDocs( 34 | query(collection(db, "users"), where(`roles.${role}`, "==", status)), 35 | ); 36 | 37 | snapshot.forEach((doc) => { 38 | output.push(doc.data().email); 39 | }); 40 | 41 | return res.json({ items: output.join(",") }, { status: 200 }); 42 | } catch (err) { 43 | return res.json( 44 | { message: `Internal Server Error: ${err}` }, 45 | { status: 500 }, 46 | ); 47 | } 48 | }; 49 | -------------------------------------------------------------------------------- /src/app/api/dashboard/ideas/route.ts: -------------------------------------------------------------------------------- 1 | import { AUTH } from "@/data/admin/dashboard"; 2 | import { authenticate } from "@/utils/auth"; 3 | import { db } from "@/utils/firebase"; 4 | import { addDoc, collection, getDocs, query } from "firebase/firestore"; 5 | import { NextResponse } from "next/server"; 6 | 7 | interface idea { 8 | title: string; 9 | languages: string[]; 10 | details: string; 11 | contact: string; 12 | } 13 | 14 | export const GET = async () => { 15 | const res = NextResponse; 16 | const { auth, message } = await authenticate(AUTH.GET); 17 | 18 | if (auth !== 200) { 19 | return res.json( 20 | { message: `Authentication Error: ${message}` }, 21 | { status: auth }, 22 | ); 23 | } 24 | 25 | try { 26 | const output: idea[] = []; 27 | 28 | const snapshot = await getDocs(query(collection(db, "ideas"))); 29 | 30 | snapshot.forEach((doc) => { 31 | output.push(doc.data() as idea); 32 | }); 33 | 34 | return res.json( 35 | { 36 | message: "OK", 37 | items: output, 38 | }, 39 | { status: 200 }, 40 | ); 41 | } catch (err) { 42 | return res.json( 43 | { message: `Internal Server Error: ${err}` }, 44 | { status: 500 }, 45 | ); 46 | } 47 | }; 48 | 49 | export const POST = async (req: Request) => { 50 | const { auth, message } = await authenticate(AUTH.POST); 51 | 52 | const res = NextResponse; 53 | 54 | if (auth !== 200) { 55 | return res.json( 56 | { message: `Authentication Error: ${message}` }, 57 | { status: auth }, 58 | ); 59 | } 60 | 61 | const { idea, languages, details, contact } = await req.json(); 62 | 63 | try { 64 | await addDoc(collection(db, "ideas"), { 65 | title: idea, 66 | languages, 67 | details, 68 | contact, 69 | }); 70 | return res.json({ message: "OK" }, { status: 200 }); 71 | } catch (err) { 72 | return res.json( 73 | { message: `Internal Server Error: ${err}` }, 74 | { status: 500 }, 75 | ); 76 | } 77 | }; 78 | -------------------------------------------------------------------------------- /src/app/api/notes/route.js: -------------------------------------------------------------------------------- 1 | import { NextResponse } from "next/server"; 2 | import { authenticate } from "@/utils/auth"; 3 | import { AUTH } from "@/data/admin/dashboard"; 4 | import { getDocs, collection } from "firebase/firestore"; 5 | import { db } from "@/utils/firebase"; 6 | 7 | export const GET = async () => { 8 | const res = NextResponse; 9 | const { auth, message } = await authenticate(AUTH.GET); 10 | 11 | if (auth !== 200) { 12 | return res.json( 13 | { message: `Authentication Error: ${message}` }, 14 | { status: auth }, 15 | ); 16 | } 17 | 18 | try { 19 | const output = []; 20 | const snapshot = await getDocs(collection(db, "teams")); 21 | 22 | snapshot.forEach((doc) => { 23 | const { name, table, rounds, links } = doc.data(); 24 | if (rounds) { 25 | const formattedRounds = JSON.parse(rounds); 26 | 27 | output.push({ name, table, links, rounds: formattedRounds }); 28 | } 29 | }); 30 | 31 | return res.json({ message: "OK", items: output }, { status: 200 }); 32 | } catch (err) { 33 | return res.json( 34 | { message: `Internal Server Error: ${err}` }, 35 | { status: 500 }, 36 | ); 37 | } 38 | }; 39 | -------------------------------------------------------------------------------- /src/app/api/participant/route.ts: -------------------------------------------------------------------------------- 1 | import { db } from "@/utils/firebase"; 2 | import { doc, updateDoc } from "firebase/firestore"; 3 | import { authenticate } from "@/utils/auth"; 4 | import { AUTH } from "@/data/user/participant"; 5 | 6 | export const POST = async (req: Request) => { 7 | const { auth, message, user } = await authenticate(AUTH.POST); 8 | 9 | if (auth !== 200) { 10 | return Response.json( 11 | { message: `Authentication Error: ${message}` }, 12 | { status: auth }, 13 | ); 14 | } 15 | const { phone, major, age, country, school, grade, gender, shirt, diet } = 16 | await req.json(); 17 | 18 | if (!user?.id) { 19 | return Response.json( 20 | { message: "User ID is undefined after authentication." }, 21 | { status: 500 }, 22 | ); 23 | } 24 | 25 | try { 26 | await updateDoc(doc(db, "users", user.id), { 27 | phone: phone, 28 | major: major, 29 | age: age, 30 | country: country, 31 | school: school, 32 | grade: grade, 33 | gender: gender, 34 | shirt: shirt, 35 | diet: diet, 36 | }); 37 | return Response.json({ message: "OK" }, { status: 200 }); 38 | } catch (err) { 39 | return Response.json( 40 | { message: `Internal Server Error: ${err}` }, 41 | { status: 500 }, 42 | ); 43 | } 44 | }; 45 | -------------------------------------------------------------------------------- /src/app/api/settings/route.js: -------------------------------------------------------------------------------- 1 | import { 2 | collection, 3 | doc, 4 | query, 5 | setDoc, 6 | getDocs, 7 | where, 8 | } from "firebase/firestore"; 9 | import { db } from "@/utils/firebase"; 10 | import { AGES, DIETS, GENDERS, SHIRTS } from "@/data/form/information"; 11 | 12 | const roles = [ 13 | "participants", 14 | "judges", 15 | "volunteers", 16 | "mentors", 17 | "admins", 18 | "committees", 19 | "sponsors", 20 | "panelists", 21 | ]; 22 | 23 | const orders = { 24 | shirt: SHIRTS, 25 | diet: DIETS, 26 | age: AGES, 27 | gender: GENDERS, 28 | }; 29 | 30 | const getStatistic = async (role, status, statistic) => { 31 | const snapshot = await getDocs( 32 | query(collection(db, "users"), where(`roles.${role}`, "==", status)), 33 | ); 34 | 35 | const results = []; 36 | 37 | snapshot.forEach((doc) => results.push(doc.data()[statistic])); 38 | 39 | const frequency = {}; 40 | 41 | for (const option of orders[statistic]) { 42 | frequency[option] = 0; 43 | } 44 | 45 | results.forEach((value) => { 46 | frequency[value] += 1; 47 | }); 48 | 49 | return frequency; 50 | }; 51 | 52 | export const GET = async () => { 53 | const heatmaps = {}; 54 | 55 | for (const statistic of Object.keys(orders)) { 56 | heatmaps[statistic] = {}; 57 | 58 | for (const role of roles) { 59 | heatmaps[statistic][role] = { 60 | 0: {}, 61 | 1: {}, 62 | "-1": {}, 63 | }; 64 | 65 | heatmaps[statistic][role]["-1"] = await getStatistic(role, -1, statistic); 66 | heatmaps[statistic][role]["0"] = await getStatistic(role, 0, statistic); 67 | heatmaps[statistic][role]["1"] = await getStatistic(role, 1, statistic); 68 | } 69 | } 70 | 71 | await setDoc(doc(db, "statistics", "shirt"), heatmaps["shirt"]); 72 | await setDoc(doc(db, "statistics", "gender"), heatmaps["gender"]); 73 | await setDoc(doc(db, "statistics", "age"), heatmaps["age"]); 74 | await setDoc(doc(db, "statistics", "diet"), heatmaps["diet"]); 75 | 76 | return Response.json(heatmaps); 77 | }; 78 | -------------------------------------------------------------------------------- /src/app/api/statistics/route.ts: -------------------------------------------------------------------------------- 1 | import { db } from "@/utils/firebase"; 2 | import { collection, getDocs } from "firebase/firestore"; 3 | import { AGES, DIETS, GENDERS, SHIRTS } from "@/data/form/information"; 4 | 5 | const labels: string[] = [ 6 | "participants", 7 | "judges", 8 | "volunteers", 9 | "mentors", 10 | "admins", 11 | "committees", 12 | "sponsors", 13 | "panelists", 14 | ]; 15 | 16 | const orders: Record = { 17 | shirt: SHIRTS, 18 | diet: DIETS, 19 | age: AGES, 20 | gender: GENDERS, 21 | }; 22 | 23 | const statuses: string[] = ["-1", "0", "1"]; 24 | 25 | type heatmap = Record>>; 26 | 27 | export const GET = async () => { 28 | const snapshot = await getDocs(collection(db, "statistics")); 29 | 30 | const heatmaps: heatmap = {}; 31 | 32 | snapshot.forEach((doc) => { 33 | const data = doc.data(); 34 | heatmaps[doc.id] = {}; 35 | 36 | labels.forEach((label: string) => { 37 | heatmaps[doc.id][label] = {}; 38 | 39 | statuses.forEach((status: string) => { 40 | heatmaps[doc.id][label][status] = []; 41 | 42 | const results = data[label][status]; 43 | const values: number[] = orders[doc.id].map((key) => results[key]); 44 | 45 | heatmaps[doc.id][label][status] = values; 46 | }); 47 | }); 48 | }); 49 | 50 | return Response.json(heatmaps); 51 | }; 52 | -------------------------------------------------------------------------------- /src/app/api/teams/ideas/route.ts: -------------------------------------------------------------------------------- 1 | import { authenticate } from "@/utils/auth"; 2 | import { addDoc, collection } from "firebase/firestore"; 3 | import { db } from "@/utils/firebase"; 4 | 5 | export const POST = async (req: Request) => { 6 | const { auth, message } = await authenticate({ 7 | participants: [1], 8 | }); 9 | 10 | if (auth !== 200) { 11 | return Response.json( 12 | { message: `Authentication Error: ${message}` }, 13 | { status: auth }, 14 | ); 15 | } 16 | 17 | const { idea, languages, details, contact } = await req.json(); 18 | 19 | try { 20 | await addDoc(collection(db, "ideas"), { 21 | idea, 22 | languages, 23 | details, 24 | contact, 25 | }); 26 | return Response.json({ message: "OK" }, { status: 200 }); 27 | } catch (err) { 28 | return Response.json( 29 | { message: `Internal Server Error: ${err}` }, 30 | { status: 500 }, 31 | ); 32 | } 33 | }; 34 | -------------------------------------------------------------------------------- /src/app/apply/admin/page.tsx: -------------------------------------------------------------------------------- 1 | import Admin from "@/components/form/admin"; 2 | 3 | export const metadata = { 4 | title: "Form | Admin", 5 | }; 6 | 7 | export default Admin; 8 | -------------------------------------------------------------------------------- /src/app/apply/committee/page.tsx: -------------------------------------------------------------------------------- 1 | import Committee from "@/components/form/committee"; 2 | 3 | export const metadata = { 4 | title: "Form | Committee", 5 | }; 6 | 7 | export default Committee; 8 | -------------------------------------------------------------------------------- /src/app/apply/feedback/page.tsx: -------------------------------------------------------------------------------- 1 | import Feedback from "@/components/form/feedback"; 2 | 3 | export const metadata = { 4 | title: "Form | Feedback", 5 | }; 6 | 7 | export default Feedback; 8 | -------------------------------------------------------------------------------- /src/app/apply/idea/page.tsx: -------------------------------------------------------------------------------- 1 | import Ideas from "@/components/form/ideas"; 2 | 3 | export const metadata = { 4 | title: "Form | Ideas", 5 | }; 6 | 7 | export default Ideas; 8 | -------------------------------------------------------------------------------- /src/app/apply/interest/page.tsx: -------------------------------------------------------------------------------- 1 | import Interest from "@/components/form/interest"; 2 | 3 | export const metadata = { 4 | title: "Form | Interest", 5 | }; 6 | 7 | export default Interest; 8 | -------------------------------------------------------------------------------- /src/app/apply/layout.tsx: -------------------------------------------------------------------------------- 1 | import Providers from "@/components/providers"; 2 | import { Toaster } from "react-hot-toast"; 3 | import ProtectedPage from "@/components/protected"; 4 | import { getSession } from "@/utils/auth"; 5 | 6 | type Props = { 7 | children: React.ReactNode; 8 | }; 9 | 10 | const FormLayout = async ({ children }: Props) => { 11 | const session = await getSession(); 12 | 13 | return ( 14 | 15 | 16 | 17 | {children} 18 | 19 | 20 | ); 21 | }; 22 | 23 | export default FormLayout; 24 | -------------------------------------------------------------------------------- /src/app/apply/lead/page.tsx: -------------------------------------------------------------------------------- 1 | import Lead from "@/components/form/lead"; 2 | 3 | export const metadata = { 4 | title: "Form | Lead", 5 | }; 6 | 7 | export default Lead; 8 | -------------------------------------------------------------------------------- /src/app/apply/mentor/page.tsx: -------------------------------------------------------------------------------- 1 | import Mentor from "@/components/form/mentor"; 2 | 3 | export const metadata = { 4 | title: "Form | Mentor", 5 | }; 6 | 7 | export default Mentor; 8 | -------------------------------------------------------------------------------- /src/app/apply/panel/page.tsx: -------------------------------------------------------------------------------- 1 | import Panel from "@/components/form/panel"; 2 | 3 | export const metadata = { 4 | title: "Form | Panel", 5 | }; 6 | 7 | export default Panel; 8 | -------------------------------------------------------------------------------- /src/app/apply/participant/page.tsx: -------------------------------------------------------------------------------- 1 | import Participant from "@/components/form/participant"; 2 | 3 | export const metadata = { 4 | title: "Form | Participant", 5 | }; 6 | 7 | export default Participant; 8 | -------------------------------------------------------------------------------- /src/app/apply/sponsor/page.tsx: -------------------------------------------------------------------------------- 1 | import Sponsor from "@/components/form/sponsor"; 2 | 3 | export const metadata = { 4 | title: "Form | Sponsor", 5 | }; 6 | 7 | export default Sponsor; 8 | -------------------------------------------------------------------------------- /src/app/apply/volunteer/page.tsx: -------------------------------------------------------------------------------- 1 | import Volunteer from "@/components/form/volunteer"; 2 | 3 | export const metadata = { 4 | title: "Form | Volunteer", 5 | }; 6 | 7 | export default Volunteer; 8 | -------------------------------------------------------------------------------- /src/app/auth/error/page.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import Error from "@/components/error"; 3 | import { useParams } from "next/navigation"; 4 | 5 | interface Params { 6 | error?: string; 7 | } 8 | 9 | const AuthError = () => { 10 | const { error } = useParams() as Params; 11 | 12 | return ( 13 | 19 | ); 20 | }; 21 | 22 | export default AuthError; 23 | -------------------------------------------------------------------------------- /src/app/error.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import Error from "@/components/error"; 3 | 4 | type props = { 5 | error: { 6 | code: number; 7 | error: string; 8 | name: string; 9 | message: string; 10 | dev: string; 11 | }; 12 | }; 13 | 14 | const InternalError = ({ error: { code, name, message, dev } }: props) => { 15 | return ; 16 | }; 17 | 18 | export default InternalError; 19 | -------------------------------------------------------------------------------- /src/app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/acm-ucr/aurora/e2d09fdfbfe93b499a17d2f3afaee64e9edcc525/src/app/favicon.ico -------------------------------------------------------------------------------- /src/app/globals.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | -------------------------------------------------------------------------------- /src/app/judge/end/page.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import Image from "next/image"; 3 | import image1 from "@/public/engineering/hackathon/judgeAssets3.webp"; 4 | import image2 from "@/public/engineering/hackathon/judgeAssets4.webp"; 5 | 6 | const Page = () => { 7 | return ( 8 |
9 | 10 |

11 | THANK YOU FOR JUDGING! 12 |

13 | 14 |
15 | ); 16 | }; 17 | 18 | export default Page; 19 | -------------------------------------------------------------------------------- /src/app/judge/layout.tsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable new-cap */ 2 | import Providers from "@/components/providers"; 3 | import { Toaster } from "react-hot-toast"; 4 | import { getSession } from "@/utils/auth"; 5 | import ProtectedPage from "@/components/protected"; 6 | 7 | type Props = { 8 | children: React.ReactNode; 9 | }; 10 | 11 | const JudgeLayout = async ({ children }: Props) => { 12 | const session = await getSession(); 13 | 14 | return ( 15 | 16 | 17 | 18 | {children} 19 | 20 | 21 | ); 22 | }; 23 | 24 | export default JudgeLayout; 25 | -------------------------------------------------------------------------------- /src/app/judge/page.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import { Button } from "@/components/ui/button"; 3 | import { useSession } from "next-auth/react"; 4 | import Link from "next/link"; 5 | import Image from "next/image"; 6 | import image1 from "@/public/engineering/hackathon/judgeAssets1.webp"; 7 | import image2 from "@/public/engineering/hackathon/judgeAssets2.webp"; 8 | const Page = () => { 9 | const { data: session } = useSession(); 10 | 11 | const name = session?.user?.firstName; 12 | 13 | return ( 14 |
15 | 16 |
17 |

18 | Judging Portal 19 |

20 |

Welcome {name}

21 | 22 |
23 | 26 | 27 | 30 | 31 | 34 |
35 |
36 | 37 | 38 |
39 | ); 40 | }; 41 | 42 | export default Page; 43 | -------------------------------------------------------------------------------- /src/app/judge/register/page.tsx: -------------------------------------------------------------------------------- 1 | import Judge from "@/components/form/judge"; 2 | 3 | export const metadata = { 4 | title: "Judge | Register", 5 | }; 6 | 7 | export default Judge; 8 | -------------------------------------------------------------------------------- /src/app/judge/start/[id]/page.tsx: -------------------------------------------------------------------------------- 1 | import Start from "@/components/judging/start"; 2 | 3 | interface props { 4 | params: { 5 | id: string; 6 | }; 7 | searchParams: { [key: string]: string }; 8 | } 9 | 10 | const Page = ({ params, searchParams }: props) => { 11 | const { id } = params; 12 | 13 | return ( 14 | 20 | ); 21 | }; 22 | 23 | export default Page; 24 | -------------------------------------------------------------------------------- /src/app/judge/start/page.tsx: -------------------------------------------------------------------------------- 1 | import Dashboard from "@/components/judging/start/dashboard"; 2 | 3 | const Page = () => { 4 | return ; 5 | }; 6 | 7 | export default Page; 8 | -------------------------------------------------------------------------------- /src/app/judge/structure/page.tsx: -------------------------------------------------------------------------------- 1 | const Page = () => { 2 | return
Put the Content from Judging Debriefing Slides Here
; 3 | }; 4 | 5 | export default Page; 6 | -------------------------------------------------------------------------------- /src/app/layout.tsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable new-cap */ 2 | import "./globals.css"; 3 | import { Poppins } from "next/font/google"; 4 | 5 | const poppins = Poppins({ 6 | subsets: ["latin"], 7 | display: "swap", 8 | weight: ["100", "200", "300", "400", "500", "600", "700", "800", "900"], 9 | variable: "--font-poppins", 10 | }); 11 | 12 | type Props = { 13 | children: React.ReactNode; 14 | }; 15 | 16 | const RootLayout = async ({ children }: Props) => { 17 | return ( 18 | 19 | 20 |
{children}
21 | 22 | 23 | ); 24 | }; 25 | 26 | export default RootLayout; 27 | -------------------------------------------------------------------------------- /src/app/loading.tsx: -------------------------------------------------------------------------------- 1 | import Loading from "@/components/loading"; 2 | 3 | export default Loading; 4 | -------------------------------------------------------------------------------- /src/app/manifest.tsx: -------------------------------------------------------------------------------- 1 | import data from "@/data/config"; 2 | 3 | interface Manifest { 4 | name: string; 5 | short_name: string; 6 | description: string; 7 | start_url: string; 8 | } 9 | 10 | const manifest = (): Manifest => { 11 | return { 12 | name: data.name, 13 | short_name: data.short_name, 14 | description: data.description, 15 | start_url: "/", 16 | }; 17 | }; 18 | 19 | export default manifest; 20 | -------------------------------------------------------------------------------- /src/app/not-found.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import Error from "@/components/error"; 3 | 4 | const NotFoundError = () => { 5 | return ( 6 | 11 | ); 12 | }; 13 | 14 | export default NotFoundError; 15 | -------------------------------------------------------------------------------- /src/app/page.tsx: -------------------------------------------------------------------------------- 1 | import Release from "@/components/release"; 2 | import RELEASES from "@/data/releases"; 3 | import Live from "@/components/live"; 4 | 5 | const Page = () => { 6 | return ( 7 |
8 | 9 | 10 | 11 |
12 | ); 13 | }; 14 | 15 | export default Page; 16 | 17 | export const dynamic = "force-dynamic"; 18 | -------------------------------------------------------------------------------- /src/app/resources/page.tsx: -------------------------------------------------------------------------------- 1 | import Resources from "@/components/resources/resources"; 2 | 3 | export default Resources; 4 | -------------------------------------------------------------------------------- /src/app/robots.txt: -------------------------------------------------------------------------------- 1 | User-Agent: * 2 | Allow: / 3 | Disallow: /admin/ 4 | -------------------------------------------------------------------------------- /src/app/sitemap.ts: -------------------------------------------------------------------------------- 1 | import data from "@/data/config"; 2 | 3 | const sitemap = () => { 4 | return [ 5 | { 6 | url: `${data.domain}/`, 7 | lastModified: new Date(), 8 | changeFrequency: "monthly", 9 | priority: 1, 10 | }, 11 | { 12 | url: `${data.domain}/apply/admin`, 13 | lastModified: new Date(), 14 | changeFrequency: "monthly", 15 | priority: 0.5, 16 | }, 17 | { 18 | url: `${data.domain}/apply/committee`, 19 | lastModified: new Date(), 20 | changeFrequency: "monthly", 21 | priority: 0.5, 22 | }, 23 | { 24 | url: `${data.domain}/apply/feedback`, 25 | lastModified: new Date(), 26 | changeFrequency: "monthly", 27 | priority: 0.5, 28 | }, 29 | { 30 | url: `${data.domain}/apply/judge`, 31 | lastModified: new Date(), 32 | changeFrequency: "monthly", 33 | priority: 0.5, 34 | }, 35 | { 36 | url: `${data.domain}/apply/mentor`, 37 | lastModified: new Date(), 38 | changeFrequency: "monthly", 39 | priority: 0.5, 40 | }, 41 | { 42 | url: `${data.domain}/apply/participant`, 43 | lastModified: new Date(), 44 | changeFrequency: "monthly", 45 | priority: 0.5, 46 | }, 47 | { 48 | url: `${data.domain}/apply/volunteer`, 49 | lastModified: new Date(), 50 | changeFrequency: "monthly", 51 | priority: 0.5, 52 | }, 53 | { 54 | url: `${data.domain}/apply/sponsor`, 55 | lastModified: new Date(), 56 | changeFrequency: "monthly", 57 | priority: 0.5, 58 | }, 59 | { 60 | url: `${data.domain}/apply/panel`, 61 | lastModified: new Date(), 62 | changeFrequency: "monthly", 63 | priority: 0.5, 64 | }, 65 | { 66 | url: `${data.domain}/user/checkin`, 67 | lastModified: new Date(), 68 | changeFrequency: "monthly", 69 | priority: 0.5, 70 | }, 71 | { 72 | url: `${data.domain}/user/dashboard`, 73 | lastModified: new Date(), 74 | changeFrequency: "monthly", 75 | priority: 0.5, 76 | }, 77 | ]; 78 | }; 79 | 80 | export default sitemap; 81 | -------------------------------------------------------------------------------- /src/app/user/checkin/page.tsx: -------------------------------------------------------------------------------- 1 | import CheckIn from "@/components/user/checkIn"; 2 | 3 | export const metadata = { 4 | title: "User | CheckIn", 5 | }; 6 | 7 | export default CheckIn; 8 | -------------------------------------------------------------------------------- /src/app/user/dashboard/page.tsx: -------------------------------------------------------------------------------- 1 | import Dashboard from "@/components/user/dashboard"; 2 | 3 | export const metadata = { 4 | title: "User | Dashboard", 5 | }; 6 | 7 | export default Dashboard; 8 | -------------------------------------------------------------------------------- /src/app/user/find/page.tsx: -------------------------------------------------------------------------------- 1 | import Find from "@/components/user/find"; 2 | 3 | export const metadata = { 4 | title: "User | Find", 5 | }; 6 | 7 | export default Find; 8 | -------------------------------------------------------------------------------- /src/app/user/join/[team]/page.tsx: -------------------------------------------------------------------------------- 1 | import Join from "@/components/user/join"; 2 | 3 | type props = { 4 | params: { team: string }; 5 | }; 6 | 7 | export const metadata = { 8 | title: "User | Join", 9 | }; 10 | 11 | const Page = ({ params }: props) => { 12 | return ; 13 | }; 14 | 15 | export default Page; 16 | -------------------------------------------------------------------------------- /src/app/user/layout.tsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable new-cap */ 2 | import Providers from "@/components/providers"; 3 | import { Toaster } from "react-hot-toast"; 4 | import { getSession } from "@/utils/auth"; 5 | import ProtectedPage from "@/components/protected"; 6 | 7 | type Props = { 8 | children: React.ReactNode; 9 | }; 10 | 11 | const UserLayout = async ({ children }: Props) => { 12 | const session = await getSession(); 13 | 14 | return ( 15 | 16 | 17 | 18 | {children} 19 | 20 | 21 | ); 22 | }; 23 | 24 | export default UserLayout; 25 | -------------------------------------------------------------------------------- /src/app/user/schedule/page.tsx: -------------------------------------------------------------------------------- 1 | import Schedule from "@/components/user/schedule/Wrapper"; 2 | 3 | export const metadata = { 4 | title: "User | Schedule", 5 | }; 6 | 7 | export default Schedule; 8 | 9 | export const dynamic = "force-dynamic"; 10 | -------------------------------------------------------------------------------- /src/app/user/team/page.tsx: -------------------------------------------------------------------------------- 1 | import Team from "@/components/user/team/team"; 2 | 3 | export const metadata = { 4 | title: "User | Team", 5 | }; 6 | 7 | export default Team; 8 | -------------------------------------------------------------------------------- /src/app/visualize/page.tsx: -------------------------------------------------------------------------------- 1 | import Visualizer from "next-route-visualizer"; 2 | 3 | const Page = () => ; 4 | export default Page; 5 | -------------------------------------------------------------------------------- /src/components/admin/dashboards/admins.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import { TAGS, COLUMNS, SUBCOLUMNS } from "@/data/admin/admins"; 3 | import { STATUSES } from "@/data/statuses"; 4 | import Table from "./dashboard/dashboard"; 5 | import { SearchParams } from "@/types/dashboard"; 6 | 7 | type props = { 8 | searchParams: SearchParams; 9 | }; 10 | 11 | const Admin = ({ searchParams }: props) => { 12 | return ( 13 |
14 | 22 | 23 | ); 24 | }; 25 | export default Admin; 26 | -------------------------------------------------------------------------------- /src/components/admin/dashboards/committees.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import { TAGS, COLUMNS } from "@/data/admin/committees"; 3 | import { STATUSES } from "@/data/statuses"; 4 | import Table from "./dashboard/dashboard"; 5 | import { SearchParams } from "@/types/dashboard"; 6 | 7 | type props = { 8 | searchParams: SearchParams; 9 | }; 10 | 11 | const Committee = ({ searchParams }: props) => { 12 | return ( 13 |
14 |
22 | 23 | ); 24 | }; 25 | export default Committee; 26 | -------------------------------------------------------------------------------- /src/components/admin/dashboards/dashboard/filters.jsx: -------------------------------------------------------------------------------- 1 | import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group"; 2 | import { COLORS } from "@/data/tags"; 3 | import { cn } from "@/utils/tailwind"; 4 | 5 | const Filters = ({ statuses, filters, setFilters }) => { 6 | const selectedFilters = 7 | filters.find(({ id }) => id === "status")?.value || []; 8 | 9 | const onClick = (value, isActive) => { 10 | setFilters((prev) => { 11 | const statuses = prev.find(({ id }) => id === "status")?.value; 12 | if (!statuses) { 13 | return prev.concat({ 14 | id: "status", 15 | value: [value], 16 | }); 17 | } 18 | 19 | return prev.map((f) => 20 | f.id === "status" 21 | ? { 22 | id: "status", 23 | value: isActive 24 | ? statuses.filter((s) => s !== value) 25 | : statuses.concat(value), 26 | } 27 | : f, 28 | ); 29 | }); 30 | }; 31 | 32 | return ( 33 |
34 | 35 | {Object.entries(statuses).map(([key, value]) => ( 36 | 40 | onClick(parseInt(key), selectedFilters.includes(parseInt(key))) 41 | } 42 | className={cn( 43 | COLORS["gray"]?.background, 44 | COLORS["gray"]?.text, 45 | COLORS["gray"]?.hover, 46 | "capitalize", 47 | )} 48 | > 49 | {value} 50 | 51 | ))} 52 | 53 |
54 | ); 55 | }; 56 | 57 | export default Filters; 58 | -------------------------------------------------------------------------------- /src/components/admin/dashboards/dashboard/view.jsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import { useState } from "react"; 3 | import { Download } from "lucide-react"; 4 | import { download } from "@/utils/download"; 5 | import { Badge } from "@/components/ui/badge"; 6 | import { 7 | Dialog, 8 | DialogContent, 9 | DialogHeader, 10 | DialogTitle, 11 | } from "@/components/ui/dialog"; 12 | 13 | const View = ({ title, src, type }) => { 14 | const [modal, setModal] = useState({ 15 | title: "", 16 | src: "", 17 | visible: false, 18 | }); 19 | 20 | const openPDF = () => { 21 | const byteCharacters = atob(src.split(",")[1]); 22 | const byteNumbers = new Array(byteCharacters.length); 23 | 24 | for (let i = 0; i < byteCharacters.length; i++) { 25 | byteNumbers[i] = byteCharacters.charCodeAt(i); 26 | } 27 | 28 | const byteArray = new Uint8Array(byteNumbers); 29 | const blob = new Blob([byteArray], { type: "application/pdf" }); 30 | 31 | const blobUrl = URL.createObjectURL(blob); 32 | window.open(blobUrl, "_blank"); 33 | }; 34 | 35 | return ( 36 |
37 | setModal({ src, title, visible: value })} 40 | > 41 | 42 | 43 | {modal.title} 44 | 45 | 53 | 54 | 55 | 56 | 59 | type === "photo" ? setModal({ src, title, visible: true }) : openPDF() 60 | } 61 | > 62 | view 63 | 64 | 65 | 68 | download( 69 | src, 70 | `${title.replace(" ", "_")}.${type === "photo" ? "png" : "pdf"}`, 71 | ) 72 | } 73 | /> 74 |
75 | ); 76 | }; 77 | 78 | export default View; 79 | -------------------------------------------------------------------------------- /src/components/admin/dashboards/feedback.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import { STATUSES, TAGS, COLUMNS } from "@/data/admin/feedback"; 3 | import Table from "./dashboard/dashboard"; 4 | import { SearchParams } from "@/types/dashboard"; 5 | 6 | type props = { 7 | searchParams: SearchParams; 8 | }; 9 | 10 | const Feedback = ({ searchParams }: props) => { 11 | return ( 12 |
13 |
21 | 22 | ); 23 | }; 24 | export default Feedback; 25 | -------------------------------------------------------------------------------- /src/components/admin/dashboards/interests.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import { TAGS, COLUMNS } from "@/data/admin/interests"; 3 | import { STATUSES } from "@/data/statuses"; 4 | import Table from "./dashboard/dashboard"; 5 | import { SearchParams } from "@/types/dashboard"; 6 | 7 | type props = { 8 | searchParams: SearchParams; 9 | }; 10 | 11 | const Interests = ({ searchParams }: props) => { 12 | return ( 13 |
14 |
22 | 23 | ); 24 | }; 25 | export default Interests; 26 | -------------------------------------------------------------------------------- /src/components/admin/dashboards/judges.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import { TAGS, COLUMNS } from "@/data/admin/judges"; 3 | import { STATUSES } from "@/data/statuses"; 4 | import Table from "./dashboard/dashboard"; 5 | import { SearchParams } from "@/types/dashboard"; 6 | 7 | type props = { 8 | searchParams: SearchParams; 9 | }; 10 | 11 | const Judges = ({ searchParams }: props) => { 12 | return ( 13 |
14 |
22 | 23 | ); 24 | }; 25 | export default Judges; 26 | -------------------------------------------------------------------------------- /src/components/admin/dashboards/leads.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import { TAGS, COLUMNS } from "@/data/admin/leads"; 3 | import { STATUSES } from "@/data/statuses"; 4 | import Table from "./dashboard/dashboard"; 5 | import { SearchParams } from "@/types/dashboard"; 6 | 7 | type props = { 8 | searchParams: SearchParams; 9 | }; 10 | 11 | const Leads = ({ searchParams }: props) => { 12 | return ( 13 |
14 |
22 | 23 | ); 24 | }; 25 | export default Leads; 26 | -------------------------------------------------------------------------------- /src/components/admin/dashboards/mentors.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import { TAGS, COLUMNS } from "@/data/admin/mentors"; 3 | import { STATUSES } from "@/data/statuses"; 4 | import Table from "./dashboard/dashboard"; 5 | import { SearchParams } from "@/types/dashboard"; 6 | 7 | type props = { 8 | searchParams: SearchParams; 9 | }; 10 | 11 | const Mentors = ({ searchParams }: props) => { 12 | return ( 13 |
14 |
22 | 23 | ); 24 | }; 25 | export default Mentors; 26 | -------------------------------------------------------------------------------- /src/components/admin/dashboards/panels.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import { TAGS, COLUMNS } from "@/data/admin/panelists"; 3 | import { STATUSES } from "@/data/statuses"; 4 | import Table from "./dashboard/dashboard"; 5 | import { SearchParams } from "@/types/dashboard"; 6 | 7 | type props = { 8 | searchParams: SearchParams; 9 | }; 10 | 11 | const Panels = ({ searchParams }: props) => { 12 | return ( 13 |
14 |
22 | 23 | ); 24 | }; 25 | export default Panels; 26 | -------------------------------------------------------------------------------- /src/components/admin/dashboards/participants.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import { TAGS, COLUMNS, SUBCOLUMNS } from "@/data/admin/participants"; 3 | import { STATUSES } from "@/data/statuses"; 4 | import Table from "./dashboard/dashboard"; 5 | import { SearchParams } from "@/types/dashboard"; 6 | 7 | type props = { 8 | searchParams: SearchParams; 9 | }; 10 | 11 | const Participants = ({ searchParams }: props) => { 12 | return ( 13 |
14 |
22 | 23 | ); 24 | }; 25 | export default Participants; 26 | -------------------------------------------------------------------------------- /src/components/admin/dashboards/resumes.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import { TAGS, COLUMNS } from "@/data/admin/resumes"; 3 | import { STATUSES } from "@/data/statuses"; 4 | import Table from "./dashboard/dashboard"; 5 | import { SearchParams } from "@/types/dashboard"; 6 | 7 | type props = { 8 | searchParams: SearchParams; 9 | }; 10 | 11 | const Resumes = ({ searchParams }: props) => { 12 | return ( 13 |
14 |
22 | 23 | ); 24 | }; 25 | export default Resumes; 26 | -------------------------------------------------------------------------------- /src/components/admin/dashboards/sponsors.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import { TAGS, COLUMNS } from "@/data/admin/sponsors"; 3 | import { STATUSES } from "@/data/statuses"; 4 | import Table from "./dashboard/dashboard"; 5 | import { SearchParams } from "@/types/dashboard"; 6 | 7 | type props = { 8 | searchParams: SearchParams; 9 | }; 10 | 11 | const Sponsors = ({ searchParams }: props) => { 12 | return ( 13 |
14 |
22 | 23 | ); 24 | }; 25 | export default Sponsors; 26 | -------------------------------------------------------------------------------- /src/components/admin/dashboards/teams.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import { TAGS, COLUMNS } from "@/data/admin/teams"; 3 | import { STATUSES } from "@/data/statuses"; 4 | import Table from "./dashboard/dashboard"; 5 | import { SearchParams } from "@/types/dashboard"; 6 | 7 | type props = { 8 | searchParams: SearchParams; 9 | }; 10 | 11 | const Teams = ({ searchParams }: props) => { 12 | return ( 13 |
14 |
22 | 23 | ); 24 | }; 25 | export default Teams; 26 | -------------------------------------------------------------------------------- /src/components/admin/dashboards/volunteers.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import { TAGS, COLUMNS } from "@/data/admin/volunteers"; 3 | import { STATUSES } from "@/data/statuses"; 4 | import Table from "./dashboard/dashboard"; 5 | import { SearchParams } from "@/types/dashboard"; 6 | 7 | type props = { 8 | searchParams: SearchParams; 9 | }; 10 | 11 | const Volunteers = ({ searchParams }: props) => { 12 | return ( 13 |
14 |
22 | 23 | ); 24 | }; 25 | export default Volunteers; 26 | -------------------------------------------------------------------------------- /src/components/admin/services/calendar/actions.ts: -------------------------------------------------------------------------------- 1 | import { LABELS, EventTypes } from "@/data/admin/calendar"; 2 | import { api } from "@/utils/api"; 3 | import { AuroraEvent } from "@/types/calendar"; 4 | const min = new Date( 5 | new Date().getTime() - 20 * 7 * 24 * 60 * 60 * 1000, 6 | ).toISOString(); 7 | 8 | const max = new Date( 9 | new Date().getTime() + 20 * 7 * 24 * 60 * 60 * 1000, 10 | ).toISOString(); 11 | 12 | export const getEvents = async () => { 13 | const hackathonResponse = await api({ 14 | method: "GET", 15 | url: `https://www.googleapis.com/calendar/v3/calendars/${process.env.NEXT_PUBLIC_GOOGLE_CALENDAR}/events?key=${process.env.NEXT_PUBLIC_GOOGLE_CALENDAR_API_KEY}&singleEvents=true&orderBy=startTime&timeMin=${min}&timeMax=${max}`, 16 | }); 17 | 18 | const leadsResponse = await api({ 19 | method: "GET", 20 | url: `https://www.googleapis.com/calendar/v3/calendars/${process.env.NEXT_PUBLIC_GOOGLE_CALENDAR_LEADS}/events?key=${process.env.NEXT_PUBLIC_GOOGLE_CALENDAR_API_KEY}&singleEvents=true&orderBy=startTime&timeMin=${min}&timeMax=${max}`, 21 | }); 22 | 23 | const items = [...hackathonResponse.items, leadsResponse.items][0]; 24 | items.forEach( 25 | ( 26 | item: AuroraEvent & { 27 | start: { dateTime: string }; 28 | end: { dateTime: string }; 29 | }, 30 | ) => { 31 | item.startDate = new Date(item.start.dateTime); 32 | item.endDate = new Date(item.end.dateTime); 33 | let category: EventTypes = "other"; 34 | let assignee: string = ""; 35 | if (item.description) { 36 | [category, assignee] = item.description 37 | .split("\n")[0] 38 | .split("#") 39 | .map((item: string) => item.trim()) 40 | .filter((item: string) => item !== "") as [EventTypes, string]; 41 | } else { 42 | item.description = "N/A"; 43 | } 44 | if (category in LABELS) { 45 | item.color = LABELS[category].background; 46 | } else { 47 | category = "other"; 48 | item.color = "!bg-hackathon-tags-gray-text"; 49 | } 50 | item.category = category; 51 | item.assignee = assignee; 52 | item.hidden = false; 53 | }, 54 | ); 55 | 56 | return items; 57 | }; 58 | -------------------------------------------------------------------------------- /src/components/admin/services/calendar/event.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | interface EventProps { 4 | event: { 5 | startDate: Date; 6 | summary: string; 7 | }; 8 | view: string; 9 | } 10 | 11 | const Event: React.FC = ({ event, view }) => { 12 | return ( 13 |
14 |

15 | {view === "month" && ( 16 | <> 17 | {new Date(event.startDate).toLocaleTimeString(navigator.language, { 18 | hour: "2-digit", 19 | minute: "2-digit", 20 | })} 21 | {" - "} 22 | 23 | )} 24 | {event.summary} 25 |

26 |
27 | ); 28 | }; 29 | 30 | export default Event; 31 | -------------------------------------------------------------------------------- /src/components/admin/services/calendar/index.tsx: -------------------------------------------------------------------------------- 1 | import { ReactQuery } from "@/utils/react-query"; 2 | import { getEvents } from "./actions"; 3 | import Calendar from "./calendar"; 4 | 5 | const Index = () => { 6 | return ( 7 | 8 | 9 | 10 | ); 11 | }; 12 | 13 | export default Index; 14 | -------------------------------------------------------------------------------- /src/components/admin/services/calendar/modal.tsx: -------------------------------------------------------------------------------- 1 | import { X, MapPin, User } from "lucide-react"; 2 | import { Badge } from "@/components/ui/badge"; 3 | import { AuroraEvent } from "@/types/calendar"; 4 | 5 | interface props { 6 | event: AuroraEvent | null; 7 | setEvent: (event: AuroraEvent | null) => void; 8 | } 9 | 10 | const Modal = ({ event, setEvent }: props) => { 11 | return ( 12 | event && ( 13 |
14 |
17 | {event.summary} 18 | setEvent(null)} 20 | className="text-xl text-white hover:cursor-pointer hover:!text-red-500" 21 | /> 22 |
23 |
24 |
25 |
26 | {event.startDate.toLocaleString("default", { 27 | month: "long", 28 | weekday: "long", 29 | day: "2-digit", 30 | year: "numeric", 31 | })} 32 |
33 | {event.startDate.toLocaleString("default", { 34 | hour: "numeric", 35 | minute: "2-digit", 36 | })} 37 |
38 | 39 |
40 | {event.category} 41 |
42 |
43 |
44 | 45 | {event.location ? event.location : "No Location Specified"} 46 |
47 |
48 | 49 | {event.assignee} 50 |
51 |

{event.description.split("\n")[1]}

52 |
53 |
54 | ) 55 | ); 56 | }; 57 | 58 | export default Modal; 59 | -------------------------------------------------------------------------------- /src/components/admin/services/checkin/actions.ts: -------------------------------------------------------------------------------- 1 | import { api } from "@/utils/api"; 2 | 3 | export const getEvents = async () => { 4 | const { items } = await api({ 5 | method: "GET", 6 | url: `https://www.googleapis.com/calendar/v3/calendars/${process.env.NEXT_PUBLIC_GOOGLE_CALENDAR}/events?key=${process.env.NEXT_PUBLIC_GOOGLE_CALENDAR_API_KEY}&singleEvents=true&orderBy=startTime`, 7 | }); 8 | 9 | return items; 10 | }; 11 | 12 | export const getUser = async (user: string | null) => { 13 | const { items } = await api({ 14 | method: "GET", 15 | url: `/api/checkin?uid=${user}`, 16 | }); 17 | 18 | return items; 19 | }; 20 | -------------------------------------------------------------------------------- /src/components/admin/services/checkin/index.tsx: -------------------------------------------------------------------------------- 1 | import Checkin from "./checkIn"; 2 | import { getEvents } from "./actions"; 3 | import { ReactQuery } from "@/utils/react-query"; 4 | 5 | const Index = async () => { 6 | return ( 7 | 8 | 9 | 10 | ); 11 | }; 12 | 13 | export default Index; 14 | -------------------------------------------------------------------------------- /src/components/admin/services/checkin/scanner.tsx: -------------------------------------------------------------------------------- 1 | import { useZxing } from "react-zxing"; 2 | 3 | type Props = { 4 | setResult: (value: string) => void; 5 | }; 6 | 7 | const ScanQRCode = ({ setResult }: Props) => { 8 | const { ref } = useZxing({ 9 | onDecodeResult(result) { 10 | setResult(result.getText()); 11 | }, 12 | }); 13 | 14 | return ( 15 |
35 | )} 36 | 37 | ); 38 | }; 39 | 40 | export default Judging; 41 | -------------------------------------------------------------------------------- /src/components/admin/services/notes/actions.ts: -------------------------------------------------------------------------------- 1 | import { api } from "@/utils/api"; 2 | 3 | export const getResults = async () => { 4 | const { items } = await api({ 5 | method: "GET", 6 | url: `/api/notes`, 7 | }); 8 | 9 | return items; 10 | }; 11 | -------------------------------------------------------------------------------- /src/components/admin/services/notes/index.tsx: -------------------------------------------------------------------------------- 1 | import { getResults } from "./actions"; 2 | import Notes from "./notes"; 3 | import { ReactQuery } from "@/utils/react-query"; 4 | 5 | const Index = async () => { 6 | return ( 7 | 8 | 9 | 10 | ); 11 | }; 12 | 13 | export default Index; 14 | -------------------------------------------------------------------------------- /src/components/admin/services/notes/notes.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import { Label } from "@/components/ui/label"; 3 | 4 | import { useState } from "react"; 5 | import { useQuery } from "@tanstack/react-query"; 6 | import Loading from "@/components/loading"; 7 | import Rounds from "./rounds"; 8 | import Teams from "./teams"; 9 | import { getResults } from "./actions"; 10 | import { Team } from "@/types/users"; 11 | 12 | const Notes = () => { 13 | const { data, isLoading } = useQuery({ 14 | queryKey: ["/admin/notes"], 15 | queryFn: async () => await getResults(), 16 | }); 17 | const [team, setTeam] = useState(null); 18 | 19 | if (isLoading) return ; 20 | return ( 21 |
22 | 23 | {data ? ( 24 |
25 | 26 | {team ? : Select a Team} 27 |
28 | ) : ( 29 | Judging has not started yet 30 | )} 31 |
32 | ); 33 | }; 34 | 35 | export default Notes; 36 | -------------------------------------------------------------------------------- /src/components/admin/services/notes/teams.tsx: -------------------------------------------------------------------------------- 1 | import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group"; 2 | import { Team } from "@/types/users"; 3 | 4 | type props = { teams: Team[]; setTeam: (value: Team) => void }; 5 | const Teams = ({ teams, setTeam }: props) => { 6 | return ( 7 | 8 | {teams?.map(({ name, table, ...team }, index) => { 9 | return ( 10 | setTeam({ name, table, ...team })} 15 | > 16 |
17 | {table} - {name} 18 |
19 |
20 | ); 21 | })} 22 |
23 | ); 24 | }; 25 | 26 | export default Teams; 27 | -------------------------------------------------------------------------------- /src/components/admin/services/settings/index.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import { Button } from "@/components/ui/button"; 3 | import { Label } from "@/components/ui/label"; 4 | import { api } from "@/utils/api"; 5 | import toast from "react-hot-toast"; 6 | 7 | const Settings = () => { 8 | const syncStatsWithDatabase = () => { 9 | toast.promise( 10 | api({ 11 | method: "GET", 12 | url: "/api/settings", 13 | }), 14 | { 15 | loading: "Loading", 16 | success: "Statistics Synced", 17 | error: "Failed to Sync", 18 | }, 19 | ); 20 | }; 21 | 22 | return ( 23 |
24 | 25 | 26 | 27 |
28 | ); 29 | }; 30 | 31 | export default Settings; 32 | -------------------------------------------------------------------------------- /src/components/admin/services/statistics/heatmap.tsx: -------------------------------------------------------------------------------- 1 | import { Label } from "@/components/ui/label"; 2 | import { HeatMapGrid } from "react-grid-heatmap"; 3 | 4 | interface props { 5 | label: string; 6 | data: number[][]; 7 | xLabels: string[]; 8 | yLabels: string[]; 9 | } 10 | 11 | const Heatmap = ({ label, data, xLabels, yLabels }: props) => { 12 | return ( 13 |
14 | 15 | ( 20 |
{value}
21 | )} 22 | xLabelsStyle={() => ({ 23 | color: "#777", 24 | fontSize: ".8rem", 25 | })} 26 | yLabelsStyle={() => ({ 27 | fontSize: ".7rem", 28 | textTransform: "uppercase", 29 | color: "#777", 30 | })} 31 | cellStyle={(_x, _y, ratio) => ({ 32 | background: `rgb(12, 160, 44, ${ratio})`, 33 | fontSize: ".8rem", 34 | color: `rgb(0, 0, 0, ${ratio / 2 + 0.4})`, 35 | })} 36 | cellHeight="2rem" 37 | xLabelsPos="bottom" 38 | onClick={(x, y) => alert(`Clicked (${x}, ${y})`)} 39 | yLabelsPos="left" 40 | /> 41 |
42 | ); 43 | }; 44 | 45 | export default Heatmap; 46 | -------------------------------------------------------------------------------- /src/components/admin/services/timer/index.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import { useState } from "react"; 3 | import Clock from "./clock"; 4 | import { v4 as uuidv4 } from "uuid"; 5 | import { Button } from "@/components/ui/button"; 6 | import { Label } from "@/components/ui/label"; 7 | 8 | type TimerType = { 9 | id: string; 10 | }; 11 | 12 | const Timer = () => { 13 | const [timers, setTimers] = useState([]); 14 | 15 | const addTimer = () => { 16 | setTimers([ 17 | { 18 | id: uuidv4(), 19 | }, 20 | ...timers, 21 | ]); 22 | }; 23 | 24 | const clearAll = () => { 25 | setTimers([]); 26 | }; 27 | 28 | const deleteTimer = (id: string) => { 29 | setTimers(timers.filter((timer) => timer.id !== id)); 30 | }; 31 | 32 | return ( 33 |
34 |
35 | 36 | 37 | 38 | 41 |
42 |
43 | {timers.length === 0 ? ( 44 |
45 | No Timers 46 |
47 | ) : ( 48 | timers.map((timer) => ( 49 | deleteTimer(timer.id)} /> 50 | )) 51 | )} 52 |
53 |
54 | ); 55 | }; 56 | 57 | export default Timer; 58 | -------------------------------------------------------------------------------- /src/components/checkbox.tsx: -------------------------------------------------------------------------------- 1 | import { Checkbox as Check } from "./ui/checkbox"; 2 | import { Label } from "./ui/label"; 3 | 4 | type checkbox = { 5 | id: string; 6 | checked: boolean; 7 | children?: string; 8 | onClick?: (event: React.MouseEvent) => void; 9 | }; 10 | 11 | const Checkbox = ({ id, checked, onClick, children }: checkbox) => { 12 | return ( 13 |
14 | 15 | 22 |
23 | ); 24 | }; 25 | 26 | export default Checkbox; 27 | -------------------------------------------------------------------------------- /src/components/email/acceptance.tsx: -------------------------------------------------------------------------------- 1 | import data from "@/data/config"; 2 | import Template from "./template"; 3 | import { Button, Section, Text } from "@react-email/components"; 4 | 5 | interface props { 6 | name: string; 7 | position: string; 8 | preview: string; 9 | } 10 | 11 | const Acceptance = ({ name, position, preview }: props) => { 12 | return ( 13 | 33 | ); 34 | }; 35 | 36 | export default Acceptance; 37 | -------------------------------------------------------------------------------- /src/components/email/confirmation.tsx: -------------------------------------------------------------------------------- 1 | import data from "@/data/config"; 2 | import Template from "./template"; 3 | import { Text } from "@react-email/components"; 4 | 5 | interface props { 6 | name: string; 7 | position: string; 8 | preview: string; 9 | } 10 | 11 | const Confirmation = ({ name, position, preview }: props) => { 12 | return ( 13 | 22 | ); 23 | }; 24 | 25 | export default Confirmation; 26 | -------------------------------------------------------------------------------- /src/components/email/index.tsx: -------------------------------------------------------------------------------- 1 | import Acceptance from "./acceptance"; 2 | import Confirmation from "./confirmation"; 3 | import Rejection from "./rejection"; 4 | 5 | interface props { 6 | id: "confirmation" | "acceptance" | "rejection"; 7 | name: string; 8 | position: string; 9 | preview: string; 10 | } 11 | 12 | const Email = ({ id, name, position, preview }: props) => { 13 | if (id === "confirmation") 14 | return ; 15 | if (id === "acceptance") 16 | return ; 17 | if (id === "rejection") 18 | return ; 19 | }; 20 | 21 | export default Email; 22 | -------------------------------------------------------------------------------- /src/components/email/rejection.tsx: -------------------------------------------------------------------------------- 1 | import data from "@/data/config"; 2 | import Template from "./template"; 3 | import { Text } from "@react-email/components"; 4 | 5 | interface props { 6 | name: string; 7 | position: string; 8 | preview: string; 9 | } 10 | 11 | const Rejection = ({ name, position, preview }: props) => { 12 | return ( 13 | 24 | ); 25 | }; 26 | 27 | export default Rejection; 28 | -------------------------------------------------------------------------------- /src/components/error.tsx: -------------------------------------------------------------------------------- 1 | import Fault from "@/utils/error"; 2 | 3 | const Error = ({ code, name, message, dev }: Fault) => { 4 | return ( 5 |
6 |

7 | {code} 8 |

9 |

10 | {name} 11 |

12 |

13 | {message} 14 |

15 | {dev && ( 16 |

17 | Developer Notes: {dev} 18 |

19 | )} 20 |
21 | ); 22 | }; 23 | 24 | export default Error; 25 | -------------------------------------------------------------------------------- /src/components/form/admin.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { useState } from "react"; 4 | import Form from "@/components/form/form"; 5 | import { FIELDS, ATTRIBUTES } from "@/data/form/admins"; 6 | import { useSession } from "next-auth/react"; 7 | import { STATUSES } from "@/data/statuses"; 8 | import { schema } from "@/schemas/admin"; 9 | import { submit } from "@/utils/form"; 10 | 11 | const Admin = () => { 12 | const { data: session } = useSession(); 13 | 14 | const [admin, setAdmin] = useState({ 15 | ...ATTRIBUTES, 16 | firstName: session?.user.firstName || "", 17 | lastName: session?.user.lastName || "", 18 | email: session?.user.email || "", 19 | roles: session?.user.roles || {}, 20 | form: "admins", 21 | }); 22 | 23 | if (!session?.user) return null; 24 | 25 | const onSubmit = async ( 26 | setLoading: (value: boolean) => void, 27 | setState: (value: number) => void, 28 | ) => { 29 | await submit({ 30 | data: admin, 31 | schema, 32 | url: "/api/dashboard/admins", 33 | setLoading, 34 | setState, 35 | }); 36 | }; 37 | return ( 38 |
46 | ); 47 | }; 48 | 49 | export default Admin; 50 | -------------------------------------------------------------------------------- /src/components/form/committee.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { useState } from "react"; 4 | import Form from "@/components/form/form"; 5 | import { FIELDS, ATTRIBUTES } from "@/data/form/committees"; 6 | import { useSession } from "next-auth/react"; 7 | import { STATUSES } from "@/data/statuses"; 8 | import { schema } from "@/schemas/committee"; 9 | import { submit } from "@/utils/form"; 10 | 11 | const Committee = () => { 12 | const { data: session } = useSession(); 13 | 14 | const [committee, setCommittee] = useState({ 15 | ...ATTRIBUTES, 16 | firstName: session?.user.firstName || "", 17 | lastName: session?.user.lastName || "", 18 | email: session?.user.email || "", 19 | roles: session?.user.roles || {}, 20 | form: "committees", 21 | }); 22 | 23 | if (!session?.user) return null; 24 | 25 | const onSubmit = async ( 26 | setLoading: (value: boolean) => void, 27 | setState: (value: number) => void, 28 | ) => { 29 | await submit({ 30 | data: committee, 31 | schema, 32 | url: "/api/dashboard/committees", 33 | setLoading, 34 | setState, 35 | }); 36 | }; 37 | 38 | return ( 39 | 47 | ); 48 | }; 49 | 50 | export default Committee; 51 | -------------------------------------------------------------------------------- /src/components/form/feedback.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { useState } from "react"; 4 | import Form from "@/components/form/form"; 5 | import { FIELDS, ATTRIBUTES } from "@/data/form/feedback"; 6 | import { useSession } from "next-auth/react"; 7 | import { schema } from "@/schemas/feedback"; 8 | import { submit } from "@/utils/form"; 9 | 10 | const Feedback = () => { 11 | const { data: session } = useSession(); 12 | 13 | const [feedback, setFeedback] = useState({ 14 | ...ATTRIBUTES, 15 | roles: session?.user.roles || {}, 16 | form: "feedback", 17 | }); 18 | 19 | if (!session?.user) return null; 20 | 21 | const onSubmit = async ( 22 | setLoading: (value: boolean) => void, 23 | setState: (value: number) => void, 24 | ) => { 25 | await submit({ 26 | data: feedback, 27 | schema, 28 | url: "/api/dashboard/feedback", 29 | setLoading, 30 | setState, 31 | }); 32 | }; 33 | 34 | return ( 35 | 43 | ); 44 | }; 45 | 46 | export default Feedback; 47 | -------------------------------------------------------------------------------- /src/components/form/form/confirmation.tsx: -------------------------------------------------------------------------------- 1 | import Confetti from "react-confetti"; 2 | import { useWindowSize } from "react-use"; 3 | 4 | const Confirmation = () => { 5 | const { width, height } = useWindowSize(); 6 | return ( 7 | <> 8 | 9 |

10 | Thank you for filling out the application form. Please watch out for an 11 | email with an application status update. 12 |

13 | 14 | ); 15 | }; 16 | 17 | export default Confirmation; 18 | -------------------------------------------------------------------------------- /src/components/form/form/index.jsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { useState } from "react"; 4 | import Status from "./status"; 5 | import Questions from "./questions"; 6 | import Confirmation from "./confirmation"; 7 | import { signOut } from "next-auth/react"; 8 | import Image from "next/image"; 9 | import LOGO from "@/app/favicon.ico"; 10 | import { Button } from "@/components/ui/button"; 11 | import Link from "next/link"; 12 | 13 | const Form = ({ 14 | object, 15 | setObject, 16 | header, 17 | fields, 18 | onSubmit, 19 | statuses = {}, 20 | bypass = false, 21 | packet = false, 22 | }) => { 23 | const [loading, setLoading] = useState(false); 24 | 25 | const [state, setState] = useState( 26 | typeof object.roles[object.form] !== "undefined" && !bypass ? 0 : 1, 27 | ); 28 | 29 | return ( 30 |
31 |
32 | 35 | 38 |
39 |
40 | 41 |

42 | {header} 43 |

44 |
45 |
46 | {state === 0 ? ( 47 | 48 | ) : state === 1 ? ( 49 | 59 | ) : ( 60 | 61 | )} 62 |
63 |
64 |
65 |
66 | ); 67 | }; 68 | 69 | export default Form; 70 | -------------------------------------------------------------------------------- /src/components/form/form/status.jsx: -------------------------------------------------------------------------------- 1 | import { Button } from "@/components/ui/button"; 2 | import { Badge } from "@/components/ui/badge"; 3 | 4 | const Status = ({ object, statuses, setState }) => { 5 | return ( 6 |
7 |

8 | Hello {object.firstName}, your status is currently 9 |

10 | {statuses[object.roles[object.form]]} 11 | 12 |

13 | You have already filled out the form. If you wish to change any 14 | information, please fill out the form again. Note that your status will 15 | change until approved by an admin. 16 |

17 |

18 | If you believe that your status is incorrect, please reach out to us 19 | immediately. 20 |

21 | 22 |
23 | ); 24 | }; 25 | 26 | export default Status; 27 | -------------------------------------------------------------------------------- /src/components/form/form/terms.tsx: -------------------------------------------------------------------------------- 1 | import Checkbox from "@/components/checkbox"; 2 | import { useState } from "react"; 3 | type props = { 4 | options: string[]; 5 | onClick: () => void; 6 | onMLHClick: () => void; 7 | }; 8 | 9 | const Terms = ({ options, onClick, onMLHClick }: props) => { 10 | const [checked, setChecked] = useState(false); 11 | const [mlh, setMLH] = useState(false); 12 | 13 | const onClickWithCheckBox = () => { 14 | onClick(); 15 | setChecked(!checked); 16 | }; 17 | 18 | const onClickWithCheckBoxMLH = () => { 19 | onMLHClick(); 20 | setMLH(!mlh); 21 | }; 22 | 23 | return ( 24 | <> 25 |

26 | Terms and Conditions 27 | * 28 |

29 |
    30 | {options.map((option, index) => ( 31 |
  • {option}
  • 32 | ))} 33 |
34 | 35 |
36 | 37 | By selecting this I agree to all of the above terms 38 | 39 | 40 | 41 | I authorize MLH to send me occasional emails about relevant events, 42 | career opportunities, and community announcements. 43 | 44 |
45 | 46 | ); 47 | }; 48 | 49 | export default Terms; 50 | -------------------------------------------------------------------------------- /src/components/form/ideas.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { useState } from "react"; 4 | import Form from "@/components/form/form"; 5 | import { FIELDS, ATTRIBUTES } from "@/data/form/ideas"; 6 | import { useSession } from "next-auth/react"; 7 | import { schema } from "@/schemas/idea"; 8 | import { submit } from "@/utils/form"; 9 | 10 | const Ideas = () => { 11 | const { data: session } = useSession(); 12 | 13 | const [idea, setIdea] = useState({ 14 | ...ATTRIBUTES, 15 | firstName: session?.user.firstName || "", 16 | lastName: session?.user.lastName || "", 17 | email: session?.user.email || "", 18 | roles: session?.user.roles || {}, 19 | form: "idea", 20 | }); 21 | 22 | if (!session?.user) return null; 23 | 24 | const onSubmit = async ( 25 | setLoading: (value: boolean) => void, 26 | setState: (value: number) => void, 27 | ) => { 28 | await submit({ 29 | data: idea, 30 | schema, 31 | url: "/api/dashboard/ideas", 32 | setLoading, 33 | setState, 34 | }); 35 | }; 36 | 37 | return ( 38 | 46 | ); 47 | }; 48 | 49 | export default Ideas; 50 | -------------------------------------------------------------------------------- /src/components/form/interest.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { useState } from "react"; 4 | import Form from "@/components/form/form"; 5 | import { FIELDS, ATTRIBUTES } from "@/data/form/interest"; 6 | import { useSession } from "next-auth/react"; 7 | import { STATUSES } from "@/data/statuses"; 8 | import { schema } from "@/schemas/interest"; 9 | import { submit } from "@/utils/form"; 10 | 11 | const Interest = () => { 12 | const { data: session } = useSession(); 13 | 14 | const [interest, setInterest] = useState({ 15 | ...ATTRIBUTES, 16 | firstName: session?.user.firstName || "", 17 | lastName: session?.user.lastName || "", 18 | email: session?.user.email || "", 19 | roles: session?.user.roles || {}, 20 | form: "interests", 21 | }); 22 | 23 | if (!session?.user) return null; 24 | 25 | const onSubmit = async ( 26 | setLoading: (value: boolean) => void, 27 | setState: (value: number) => void, 28 | ) => { 29 | await submit({ 30 | data: interest, 31 | schema, 32 | url: "/api/dashboard/interests", 33 | setLoading, 34 | setState, 35 | }); 36 | }; 37 | 38 | return ( 39 | 47 | ); 48 | }; 49 | 50 | export default Interest; 51 | -------------------------------------------------------------------------------- /src/components/form/judge.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { useState } from "react"; 4 | import Form from "@/components/form/form"; 5 | import { FIELDS, ATTRIBUTES } from "@/data/form/judge"; 6 | import { useSession } from "next-auth/react"; 7 | import { STATUSES } from "@/data/statuses"; 8 | import { schema } from "@/schemas/judge"; 9 | import { submit } from "@/utils/form"; 10 | 11 | const Judge = () => { 12 | const { data: session } = useSession(); 13 | 14 | const [judge, setJudge] = useState({ 15 | ...ATTRIBUTES, 16 | firstName: session?.user.firstName || "", 17 | lastName: session?.user.lastName || "", 18 | email: session?.user.email || "", 19 | roles: session?.user.roles || {}, 20 | photo: session?.user.photo ?? "", 21 | form: "judges", 22 | }); 23 | 24 | if (!session?.user) return null; 25 | 26 | const onSubmit = async ( 27 | setLoading: (value: boolean) => void, 28 | setState: (value: number) => void, 29 | ) => { 30 | await submit({ 31 | data: judge, 32 | schema, 33 | url: "/api/dashboard/judges", 34 | setLoading, 35 | setState, 36 | }); 37 | }; 38 | 39 | return ( 40 | 48 | ); 49 | }; 50 | 51 | export default Judge; 52 | -------------------------------------------------------------------------------- /src/components/form/lead.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { useState } from "react"; 4 | import Form from "@/components/form/form"; 5 | import { FIELDS, ATTRIBUTES } from "@/data/form/leads"; 6 | import { useSession } from "next-auth/react"; 7 | import { STATUSES } from "@/data/statuses"; 8 | import { schema } from "@/schemas/lead"; 9 | import { submit } from "@/utils/form"; 10 | 11 | const Lead = () => { 12 | const { data: session } = useSession(); 13 | 14 | const [lead, setLead] = useState({ 15 | ...ATTRIBUTES, 16 | firstName: session?.user.firstName || "", 17 | lastName: session?.user.lastName || "", 18 | roles: session?.user.roles || {}, 19 | email: session?.user.email || "", 20 | form: "leads", 21 | }); 22 | 23 | if (!session?.user) return null; 24 | 25 | const onSubmit = async ( 26 | setLoading: (value: boolean) => void, 27 | setState: (value: number) => void, 28 | ) => { 29 | await submit({ 30 | data: lead, 31 | schema, 32 | url: "/api/dashboard/leads", 33 | setLoading, 34 | setState, 35 | }); 36 | }; 37 | return ( 38 | 46 | ); 47 | }; 48 | 49 | export default Lead; 50 | -------------------------------------------------------------------------------- /src/components/form/mentor.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { useState } from "react"; 4 | import Form from "@/components/form/form"; 5 | import { FIELDS, ATTRIBUTES } from "@/data/form/mentors"; 6 | import { useSession } from "next-auth/react"; 7 | import { STATUSES } from "@/data/statuses"; 8 | import { schema } from "@/schemas/mentor"; 9 | import { submit } from "@/utils/form"; 10 | 11 | const Mentor = () => { 12 | const { data: session } = useSession(); 13 | 14 | const [mentor, setMentor] = useState({ 15 | ...ATTRIBUTES, 16 | firstName: session?.user.firstName || "", 17 | lastName: session?.user.lastName || "", 18 | email: session?.user.email || "", 19 | roles: session?.user.roles || {}, 20 | form: "mentors", 21 | }); 22 | 23 | if (!session?.user) return null; 24 | 25 | const onSubmit = async ( 26 | setLoading: (value: boolean) => void, 27 | setState: (value: number) => void, 28 | ) => { 29 | await submit({ 30 | data: mentor, 31 | schema, 32 | url: "/api/dashboard/mentors", 33 | setLoading, 34 | setState, 35 | }); 36 | }; 37 | return ( 38 | 46 | ); 47 | }; 48 | 49 | export default Mentor; 50 | -------------------------------------------------------------------------------- /src/components/form/panel.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { useState } from "react"; 4 | import Form from "@/components/form/form"; 5 | import { FIELDS, ATTRIBUTES } from "@/data/form/panelists"; 6 | import { useSession } from "next-auth/react"; 7 | import { STATUSES } from "@/data/statuses"; 8 | import { schema } from "@/schemas/panel"; 9 | import { submit } from "@/utils/form"; 10 | 11 | const Panel = () => { 12 | const { data: session } = useSession(); 13 | 14 | const [panel, setPanel] = useState({ 15 | ...ATTRIBUTES, 16 | firstName: session?.user.firstName || "", 17 | lastName: session?.user.lastName || "", 18 | email: session?.user.email || "", 19 | roles: session?.user.roles || {}, 20 | photo: session?.user.photo ?? "", 21 | form: "panels", 22 | }); 23 | 24 | if (!session?.user) return null; 25 | 26 | const onSubmit = async ( 27 | setLoading: (value: boolean) => void, 28 | setState: (value: number) => void, 29 | ) => { 30 | await submit({ 31 | data: panel, 32 | schema, 33 | url: "/api/dashboard/panels", 34 | setLoading, 35 | setState, 36 | }); 37 | }; 38 | 39 | return ( 40 | 48 | ); 49 | }; 50 | 51 | export default Panel; 52 | -------------------------------------------------------------------------------- /src/components/form/participant.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { useState } from "react"; 4 | import Form from "@/components/form/form"; 5 | import { FIELDS, ATTRIBUTES } from "@/data/form/participant"; 6 | import { useSession } from "next-auth/react"; 7 | import { STATUSES } from "@/data/statuses"; 8 | import { schema } from "@/schemas/participant"; 9 | import { submit } from "@/utils/form"; 10 | 11 | const Participant = () => { 12 | const { data: session } = useSession(); 13 | 14 | const [participant, setParticipant] = useState({ 15 | ...ATTRIBUTES, 16 | firstName: session?.user.firstName || "", 17 | lastName: session?.user.lastName || "", 18 | email: session?.user.email || "", 19 | roles: session?.user.roles || {}, 20 | form: "participants", 21 | }); 22 | 23 | if (!session?.user) return null; 24 | 25 | const onSubmit = async ( 26 | setLoading: (value: boolean) => void, 27 | setState: (value: number) => void, 28 | ) => { 29 | await submit({ 30 | data: participant, 31 | schema, 32 | url: "/api/dashboard/participants", 33 | setLoading, 34 | setState, 35 | }); 36 | }; 37 | 38 | return ( 39 | 47 | ); 48 | }; 49 | 50 | export default Participant; 51 | -------------------------------------------------------------------------------- /src/components/form/sponsor.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { useState } from "react"; 4 | import Form from "@/components/form/form"; 5 | import { FIELDS, ATTRIBUTES } from "@/data/form/sponsors"; 6 | import { useSession } from "next-auth/react"; 7 | import { STATUSES } from "@/data/statuses"; 8 | import { schema } from "@/schemas/sponsor"; 9 | import { submit } from "@/utils/form"; 10 | 11 | const Sponsor = () => { 12 | const { data: session } = useSession(); 13 | 14 | const [sponsor, setSponsor] = useState({ 15 | ...ATTRIBUTES, 16 | firstName: session?.user.firstName || "", 17 | lastName: session?.user.lastName || "", 18 | email: session?.user.email || "", 19 | roles: session?.user.roles || {}, 20 | form: "sponsors", 21 | }); 22 | 23 | if (!session?.user) return null; 24 | 25 | const onSubmit = async ( 26 | setLoading: (value: boolean) => void, 27 | setState: (value: number) => void, 28 | ) => { 29 | await submit({ 30 | data: sponsor, 31 | schema, 32 | url: "/api/dashboard/sponsors", 33 | setLoading, 34 | setState, 35 | }); 36 | }; 37 | return ( 38 | 46 | ); 47 | }; 48 | 49 | export default Sponsor; 50 | -------------------------------------------------------------------------------- /src/components/form/volunteer.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { useState } from "react"; 4 | import Form from "@/components/form/form"; 5 | import { FIELDS, ATTRIBUTES } from "@/data/form/volunteers"; 6 | import { useSession } from "next-auth/react"; 7 | import { STATUSES } from "@/data/statuses"; 8 | import { schema } from "@/schemas/volunteer"; 9 | import { submit } from "@/utils/form"; 10 | 11 | const Volunteer = () => { 12 | const { data: session } = useSession(); 13 | 14 | const [volunteer, setVolunteer] = useState({ 15 | ...ATTRIBUTES, 16 | firstName: session?.user.firstName || "", 17 | lastName: session?.user.lastName || "", 18 | email: session?.user.email || "", 19 | roles: session?.user.roles || {}, 20 | form: "volunteers", 21 | }); 22 | 23 | if (!session?.user) return null; 24 | 25 | const onSubmit = async ( 26 | setLoading: (value: boolean) => void, 27 | setState: (value: number) => void, 28 | ) => { 29 | await submit({ 30 | data: volunteer, 31 | schema, 32 | url: "/api/dashboard/volunteers", 33 | setLoading, 34 | setState, 35 | }); 36 | }; 37 | 38 | return ( 39 | 47 | ); 48 | }; 49 | 50 | export default Volunteer; 51 | -------------------------------------------------------------------------------- /src/components/judging/start/confirm.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | AlertDialog, 3 | AlertDialogAction, 4 | AlertDialogCancel, 5 | AlertDialogContent, 6 | AlertDialogDescription, 7 | AlertDialogFooter, 8 | AlertDialogHeader, 9 | AlertDialogTitle, 10 | AlertDialogTrigger, 11 | } from "@/components/ui/alert-dialog"; 12 | import { Button } from "@/components/ui/button"; 13 | import { useRouter } from "next/navigation"; 14 | 15 | const Confirm = () => { 16 | const router = useRouter(); 17 | return ( 18 | 19 | 20 | 21 | 22 | 23 | 24 | Ready to finish? 25 | 26 | This will take you back to the confirmation page. 27 | 28 | 29 | 30 | Cancel 31 | 32 | 33 | 34 | 35 | 36 | 37 | ); 38 | }; 39 | 40 | export default Confirm; 41 | -------------------------------------------------------------------------------- /src/components/judging/start/form.jsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import { useState } from "react"; 3 | import Questions from "./questions"; 4 | const Form = ({ object, setObject, header, fields, onSubmit, round }) => { 5 | const [loading, setLoading] = useState(false); 6 | return ( 7 |
8 |
9 |
10 |

JUDGING

11 |

ROUND {round}

12 |
13 |
14 |

{header}

15 |
16 | 24 |
25 |
26 |
27 |
28 | ); 29 | }; 30 | 31 | export default Form; 32 | -------------------------------------------------------------------------------- /src/components/judging/start/index.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import { useState } from "react"; 3 | import Form from "./form"; 4 | import { submit } from "@/utils/form"; 5 | import { schema } from "@/schemas/judging"; 6 | import { FIELDS } from "@/data/judge/form"; 7 | import { useRouter } from "next/navigation"; 8 | interface props { 9 | id: string; 10 | name: string; 11 | round: string; 12 | table: string; 13 | } 14 | 15 | const Start = ({ id, name, round, table }: props) => { 16 | const router = useRouter(); 17 | const [form, setForm] = useState({ 18 | teamId: id, 19 | teamName: name, 20 | round: round, 21 | table, 22 | tracks: [], 23 | implementation: { rating: 0, comment: "" }, 24 | idea: { rating: 0, comment: "" }, 25 | design: { rating: 0, comment: "" }, 26 | }); 27 | const onSubmit = async ( 28 | setLoading: (value: boolean) => void, 29 | setState: (value: number) => void, 30 | ) => { 31 | await submit({ 32 | data: form, 33 | schema: schema, 34 | url: "/api/judging/start", 35 | method: "PUT", 36 | setLoading, 37 | setState, 38 | }); 39 | router.push("/judge/start"); 40 | }; 41 | return ( 42 | 50 | ); 51 | }; 52 | 53 | export default Start; 54 | -------------------------------------------------------------------------------- /src/components/live/about.tsx: -------------------------------------------------------------------------------- 1 | import Title from "@/components/ui/title"; 2 | 3 | const About = () => { 4 | return ( 5 |
6 | About 7 |
8 | ); 9 | }; 10 | 11 | export default About; 12 | -------------------------------------------------------------------------------- /src/components/live/committees.tsx: -------------------------------------------------------------------------------- 1 | import Title from "@/components/ui/title"; 2 | 3 | const Committees = () => { 4 | return ( 5 |
6 | Committees 7 |
8 | ); 9 | }; 10 | 11 | export default Committees; 12 | -------------------------------------------------------------------------------- /src/components/live/faq.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | Accordion, 3 | AccordionContent, 4 | AccordionItem, 5 | AccordionTrigger, 6 | } from "@/components/ui/accordion"; 7 | import { QUESTIONS } from "@/data/faq"; 8 | import Title from "@/components/ui/title"; 9 | 10 | const FAQ = () => { 11 | return ( 12 |
13 | FAQ 14 |
15 | 16 | {QUESTIONS.map(({ question, answer }, index) => ( 17 | 18 | {question} 19 | {answer} 20 | 21 | ))} 22 | 23 |
24 |
25 | ); 26 | }; 27 | 28 | export default FAQ; 29 | -------------------------------------------------------------------------------- /src/components/live/footer.tsx: -------------------------------------------------------------------------------- 1 | const Footer = () => { 2 | return
Footer
; 3 | }; 4 | 5 | export default Footer; 6 | -------------------------------------------------------------------------------- /src/components/live/index.tsx: -------------------------------------------------------------------------------- 1 | import Landing from "./landing"; 2 | import About from "./about"; 3 | import Schedule from "./schedule"; 4 | import Tracks from "./tracks"; 5 | import Sponsors from "./sponsors"; 6 | import Team from "./team"; 7 | import Committees from "./committees"; 8 | import Judges from "./judges"; 9 | import FAQ from "./faq"; 10 | import Footer from "./footer"; 11 | 12 | const Live = () => { 13 | return ( 14 | <> 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 | 26 | ); 27 | }; 28 | 29 | export default Live; 30 | -------------------------------------------------------------------------------- /src/components/live/judges.tsx: -------------------------------------------------------------------------------- 1 | import Title from "@/components/ui/title"; 2 | 3 | const Judges = () => { 4 | return ( 5 |
6 | Judges 7 |
8 | ); 9 | }; 10 | 11 | export default Judges; 12 | -------------------------------------------------------------------------------- /src/components/live/landing.tsx: -------------------------------------------------------------------------------- 1 | import Countdown from "../ui/countdown"; 2 | const Landing = () => { 3 | return ( 4 |
5 | 12 |
13 | ); 14 | }; 15 | 16 | export default Landing; 17 | -------------------------------------------------------------------------------- /src/components/live/schedule/index.tsx: -------------------------------------------------------------------------------- 1 | import { api } from "@/utils/api"; 2 | import Events from "./events"; 3 | import Title from "@/components/ui/title"; 4 | 5 | const Schedule = async () => { 6 | const { items } = await api({ 7 | url: `https://www.googleapis.com/calendar/v3/calendars/${process.env.NEXT_PUBLIC_GOOGLE_CALENDAR}/events?key=${process.env.NEXT_PUBLIC_GOOGLE_CALENDAR_API_KEY}&singleEvents=true&orderBy=startTime`, 8 | method: "GET", 9 | }); 10 | 11 | const totalDays = [ 12 | "Monday", 13 | "Tuesday", 14 | "Wednesday", 15 | "Thursday", 16 | "Friday", 17 | "Saturday", 18 | "Sunday", 19 | ]; 20 | 21 | return ( 22 | <> 23 | Schedule 24 | 25 | 26 | ); 27 | }; 28 | 29 | export default Schedule; 30 | -------------------------------------------------------------------------------- /src/components/live/sponsors.tsx: -------------------------------------------------------------------------------- 1 | import Title from "@/components/ui/title"; 2 | 3 | const Sponsors = () => { 4 | return ( 5 |
6 | Sponsors 7 |
8 | ); 9 | }; 10 | 11 | export default Sponsors; 12 | -------------------------------------------------------------------------------- /src/components/live/team.tsx: -------------------------------------------------------------------------------- 1 | import Title from "@/components/ui/title"; 2 | 3 | const Team = () => { 4 | return ( 5 |
6 | Team 7 |
8 | ); 9 | }; 10 | 11 | export default Team; 12 | -------------------------------------------------------------------------------- /src/components/live/tracks.tsx: -------------------------------------------------------------------------------- 1 | import Title from "@/components/ui/title"; 2 | 3 | const Tracks = () => { 4 | return ( 5 |
6 | Tracks 7 |
8 | ); 9 | }; 10 | 11 | export default Tracks; 12 | -------------------------------------------------------------------------------- /src/components/loading.tsx: -------------------------------------------------------------------------------- 1 | import { Loader } from "lucide-react"; 2 | 3 | const Loading = () => { 4 | return ( 5 |
6 |

Loading...

7 | 8 |
9 | ); 10 | }; 11 | 12 | export default Loading; 13 | -------------------------------------------------------------------------------- /src/components/protected.tsx: -------------------------------------------------------------------------------- 1 | import RELEASES from "@/data/releases"; 2 | import Fault from "@/utils/error"; 3 | import Navigation from "@/components/navigation"; 4 | import { headers } from "next/headers"; 5 | import SignIn from "@/utils/signin"; 6 | import { Session as SessionType } from "next-auth"; 7 | 8 | interface props { 9 | children: React.ReactNode; 10 | restrictions: Record; 11 | session: SessionType | null; 12 | } 13 | 14 | const ProtectedPage = async ({ children, restrictions, session }: props) => { 15 | const header = headers(); 16 | const pathName = header.get("x-url") || ""; 17 | 18 | if (!session) { 19 | return ; 20 | } 21 | 22 | if (RELEASES[pathName] > new Date()) { 23 | throw new Fault( 24 | 423, 25 | "Locked Resource", 26 | "This resource has not been released", 27 | ); 28 | } 29 | 30 | if (!session.user.roles && Object.keys(restrictions).length > 0) { 31 | throw new Fault(403, "Unauthorized", "You do not have any assigned roles"); 32 | } 33 | 34 | const authorized = Object.entries(restrictions).some(([key, values]) => 35 | Array.isArray(values) 36 | ? values.includes(session.user.roles[key]) 37 | : session.user.roles[key] === values, 38 | ); 39 | 40 | if (!authorized && Object.keys(restrictions).length > 0) { 41 | throw new Fault(403, "Unauthorized", "You do not have access this page"); 42 | } 43 | 44 | const navigation = RegExp(/user\/|admin\//).test(pathName); 45 | 46 | return ( 47 | <> 48 | {navigation && } 49 |
50 |
{children}
51 |
52 | 53 | ); 54 | }; 55 | 56 | export default ProtectedPage; 57 | -------------------------------------------------------------------------------- /src/components/providers.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import { Session as SessionType } from "next-auth"; 3 | import { SessionProvider } from "next-auth/react"; 4 | import { useState } from "react"; 5 | import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; 6 | import { SidebarProvider } from "./ui/sidebar"; 7 | 8 | type props = { 9 | children: React.ReactNode; 10 | session: SessionType | null; 11 | }; 12 | 13 | const Providers = ({ children, session }: props) => { 14 | const [client] = useState( 15 | () => 16 | new QueryClient({ 17 | defaultOptions: { 18 | queries: {}, 19 | }, 20 | }), 21 | ); 22 | 23 | return ( 24 | 25 | 26 | {children} 27 | 28 | 29 | ); 30 | }; 31 | 32 | export default Providers; 33 | -------------------------------------------------------------------------------- /src/components/release.tsx: -------------------------------------------------------------------------------- 1 | type props = { 2 | release: Date; 3 | children: React.ReactNode; 4 | }; 5 | 6 | const Release = ({ release, children }: props): React.ReactNode => { 7 | if (process.env.NODE_ENV === "development") return children; 8 | 9 | return release < new Date() ? children : null; 10 | }; 11 | 12 | export default Release; 13 | -------------------------------------------------------------------------------- /src/components/resources/hackpack.tsx: -------------------------------------------------------------------------------- 1 | import { TECHSTACKS } from "@/data/user/hackpacks"; 2 | import Link from "next/link"; 3 | import { SiGithub as Github } from "@icons-pack/react-simple-icons"; 4 | import { Badge } from "@/components/ui/badge"; 5 | 6 | type props = { 7 | text: string; 8 | languages: string[]; 9 | link: string; 10 | description: string; 11 | }; 12 | 13 | const Hackpack = ({ text, languages, link, description }: props) => { 14 | return ( 15 | 20 |
21 | {text} 22 |
23 | 24 |
25 |
26 |
27 | {languages.map((language, index) => ( 28 | 29 |
30 | {TECHSTACKS[language]} 31 |
32 | {language} 33 |
34 | ))} 35 |
36 |

{description}

37 | 38 | ); 39 | }; 40 | 41 | export default Hackpack; 42 | -------------------------------------------------------------------------------- /src/components/resources/resources.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import { HACKPACKS } from "@/data/user/hackpacks"; 3 | import Toolbar from "../user/toolbar"; 4 | import Hackpack from "./hackpack"; 5 | import { useState } from "react"; 6 | import { Label } from "@/components/ui/label"; 7 | 8 | const Resources = () => { 9 | const [search, setSearch] = useState(HACKPACKS); 10 | 11 | return ( 12 |
13 |
14 | 15 | 16 |
17 | 18 |
19 | {search.map(({ title, languages, link, description }, index) => ( 20 | 27 | ))} 28 |
29 |
30 | ); 31 | }; 32 | 33 | export default Resources; 34 | -------------------------------------------------------------------------------- /src/components/ui/accordion.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import * as React from "react"; 4 | import * as AccordionPrimitive from "@radix-ui/react-accordion"; 5 | import { ChevronDown } from "lucide-react"; 6 | 7 | import { cn } from "@/utils/tailwind"; 8 | 9 | const Accordion = AccordionPrimitive.Root; 10 | 11 | const AccordionItem = React.forwardRef< 12 | React.ElementRef, 13 | React.ComponentPropsWithoutRef 14 | >(({ className, ...props }, ref) => ( 15 | 20 | )); 21 | AccordionItem.displayName = "AccordionItem"; 22 | 23 | const AccordionTrigger = React.forwardRef< 24 | React.ElementRef, 25 | React.ComponentPropsWithoutRef 26 | >(({ className, children, ...props }, ref) => ( 27 | 28 | svg]:rotate-180", 32 | className, 33 | )} 34 | {...props} 35 | > 36 | {children} 37 | 38 | 41 | 42 | 43 | )); 44 | AccordionTrigger.displayName = AccordionPrimitive.Trigger.displayName; 45 | 46 | const AccordionContent = React.forwardRef< 47 | React.ElementRef, 48 | React.ComponentPropsWithoutRef 49 | >(({ className, children, ...props }, ref) => ( 50 | 55 |
{children}
56 |
57 | )); 58 | 59 | AccordionContent.displayName = AccordionPrimitive.Content.displayName; 60 | 61 | export { Accordion, AccordionItem, AccordionTrigger, AccordionContent }; 62 | -------------------------------------------------------------------------------- /src/components/ui/badge.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { cva, type VariantProps } from "class-variance-authority"; 3 | 4 | import { cn } from "@/utils/tailwind"; 5 | 6 | import { COLORS } from "@/data/tags"; 7 | 8 | const badgeVariants = cva( 9 | "inline-flex items-center rounded border border-slate-200 px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-slate-950 focus:ring-offset-2 dark:border-slate-800 dark:focus:ring-slate-300", 10 | { 11 | variants: { 12 | variant: { 13 | default: 14 | "border-transparent text-slate-50 dark:bg-slate-50 dark:text-slate-900", 15 | secondary: 16 | "border-transparent bg-slate-100 text-slate-900 dark:bg-slate-800 dark:text-slate-50", 17 | destructive: 18 | "border-transparent bg-red-500 text-slate-50 dark:bg-red-900 dark:text-slate-50", 19 | outline: "text-slate-950 dark:text-slate-50", 20 | }, 21 | }, 22 | defaultVariants: { 23 | variant: "default", 24 | }, 25 | }, 26 | ); 27 | 28 | export interface BadgeProps 29 | extends React.HTMLAttributes, 30 | VariantProps { 31 | type?: keyof typeof COLORS; 32 | } 33 | 34 | function Badge({ className, variant, type = "gray", ...props }: BadgeProps) { 35 | const { background, text, hover } = COLORS[type]; 36 | 37 | return ( 38 |
49 | ); 50 | } 51 | 52 | export { Badge, badgeVariants }; 53 | -------------------------------------------------------------------------------- /src/components/ui/checkbox.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import * as React from "react"; 4 | import * as CheckboxPrimitive from "@radix-ui/react-checkbox"; 5 | import { Check } from "lucide-react"; 6 | 7 | import { cn } from "@/utils/tailwind"; 8 | 9 | const Checkbox = React.forwardRef< 10 | React.ElementRef, 11 | React.ComponentPropsWithoutRef 12 | >(({ className, ...props }, ref) => ( 13 | 21 | 24 | 25 | 26 | 27 | )); 28 | Checkbox.displayName = CheckboxPrimitive.Root.displayName; 29 | 30 | export { Checkbox }; 31 | -------------------------------------------------------------------------------- /src/components/ui/collapsible.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import * as CollapsiblePrimitive from "@radix-ui/react-collapsible"; 4 | 5 | const Collapsible = CollapsiblePrimitive.Root; 6 | 7 | const CollapsibleTrigger = CollapsiblePrimitive.CollapsibleTrigger; 8 | 9 | const CollapsibleContent = CollapsiblePrimitive.CollapsibleContent; 10 | 11 | export { Collapsible, CollapsibleTrigger, CollapsibleContent }; 12 | -------------------------------------------------------------------------------- /src/components/ui/input.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | 3 | import { cn } from "@/utils/tailwind"; 4 | import { X } from "lucide-react"; 5 | 6 | const Input = React.forwardRef< 7 | HTMLInputElement, 8 | React.InputHTMLAttributes 9 | >(({ className, type, ...props }, ref) => { 10 | return ( 11 | 20 | ); 21 | }); 22 | Input.displayName = "Input"; 23 | 24 | export interface InputWithClearProps 25 | extends React.InputHTMLAttributes { 26 | onClear: () => void; 27 | } 28 | 29 | const InputWithClear = React.forwardRef( 30 | ({ className, type, onClear, ...props }, ref) => { 31 | return ( 32 |
33 | 42 | 43 | 47 |
48 | ); 49 | }, 50 | ); 51 | InputWithClear.displayName = "InputWithClear"; 52 | 53 | export { Input, InputWithClear }; 54 | -------------------------------------------------------------------------------- /src/components/ui/label.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import * as React from "react"; 4 | import * as LabelPrimitive from "@radix-ui/react-label"; 5 | import { cva, type VariantProps } from "class-variance-authority"; 6 | 7 | import { cn } from "@/utils/tailwind"; 8 | 9 | const labelVariants = cva( 10 | "mb-1 m-0.2 leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70 hover:cursor-pointer ", 11 | ); 12 | 13 | const Label = React.forwardRef< 14 | React.ElementRef, 15 | React.ComponentPropsWithoutRef & 16 | VariantProps 17 | >(({ className, ...props }, ref) => ( 18 | 23 | )); 24 | Label.displayName = LabelPrimitive.Root.displayName; 25 | 26 | export { Label }; 27 | -------------------------------------------------------------------------------- /src/components/ui/progress.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import * as React from "react"; 4 | import * as ProgressPrimitive from "@radix-ui/react-progress"; 5 | 6 | import { cn } from "@/utils/tailwind"; 7 | 8 | const Progress = React.forwardRef< 9 | React.ElementRef, 10 | React.ComponentPropsWithoutRef 11 | >(({ className, value, ...props }, ref) => ( 12 | 20 | 24 | 25 | )); 26 | Progress.displayName = ProgressPrimitive.Root.displayName; 27 | 28 | export { Progress }; 29 | -------------------------------------------------------------------------------- /src/components/ui/radio-group.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import * as React from "react"; 4 | import * as RadioGroupPrimitive from "@radix-ui/react-radio-group"; 5 | import { Circle } from "lucide-react"; 6 | 7 | import { cn } from "@/utils/tailwind"; 8 | 9 | const RadioGroup = React.forwardRef< 10 | React.ElementRef, 11 | React.ComponentPropsWithoutRef 12 | >(({ className, ...props }, ref) => { 13 | return ( 14 | 19 | ); 20 | }); 21 | RadioGroup.displayName = RadioGroupPrimitive.Root.displayName; 22 | 23 | const RadioGroupItem = React.forwardRef< 24 | React.ElementRef, 25 | React.ComponentPropsWithoutRef 26 | >(({ className, ...props }, ref) => { 27 | return ( 28 | 36 | 37 | 38 | 39 | 40 | ); 41 | }); 42 | RadioGroupItem.displayName = RadioGroupPrimitive.Item.displayName; 43 | 44 | export { RadioGroup, RadioGroupItem }; 45 | -------------------------------------------------------------------------------- /src/components/ui/separator.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import * as React from "react"; 4 | import * as SeparatorPrimitive from "@radix-ui/react-separator"; 5 | 6 | import { cn } from "@/utils/tailwind"; 7 | 8 | const Separator = React.forwardRef< 9 | React.ElementRef, 10 | React.ComponentPropsWithoutRef 11 | >( 12 | ( 13 | { className, orientation = "horizontal", decorative = true, ...props }, 14 | ref, 15 | ) => ( 16 | 27 | ), 28 | ); 29 | Separator.displayName = SeparatorPrimitive.Root.displayName; 30 | 31 | export { Separator }; 32 | -------------------------------------------------------------------------------- /src/components/ui/skeleton.tsx: -------------------------------------------------------------------------------- 1 | import { cn } from "@/utils/tailwind"; 2 | 3 | function Skeleton({ 4 | className, 5 | ...props 6 | }: React.HTMLAttributes) { 7 | return ( 8 |
15 | ); 16 | } 17 | 18 | export { Skeleton }; 19 | -------------------------------------------------------------------------------- /src/components/ui/slider.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import * as React from "react"; 4 | import * as SliderPrimitive from "@radix-ui/react-slider"; 5 | 6 | import { cn } from "@/utils/tailwind"; 7 | 8 | const Slider = React.forwardRef< 9 | React.ElementRef, 10 | React.ComponentPropsWithoutRef 11 | >(({ className, ...props }, ref) => ( 12 | 20 | 21 | 22 | 23 | 24 | 25 | )); 26 | Slider.displayName = SliderPrimitive.Root.displayName; 27 | 28 | export { Slider }; 29 | -------------------------------------------------------------------------------- /src/components/ui/textarea.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | 3 | import { cn } from "@/utils/tailwind"; 4 | 5 | const Textarea = React.forwardRef< 6 | HTMLTextAreaElement, 7 | React.TextareaHTMLAttributes 8 | >(({ className, ...props }, ref) => { 9 | return ( 10 |