From 6b327a39f1d5bb6400d4f178c177cbe25a7dcd7f Mon Sep 17 00:00:00 2001 From: dangminhduc1101 Date: Thu, 27 Jun 2024 00:14:40 -0700 Subject: [PATCH 01/16] Mock global styles --- .github/workflows/ci.yml | 10 +- .vscode/settings.json | 16 +- client/README.md | 14 - client/next.config.mjs | 4 - client/public/fonts/PPSupplyMono-Regular.otf | Bin 23996 -> 0 bytes .../public/fonts/PPSupplyMono-Ultralight.otf | Bin 24808 -> 0 bytes client/public/next.svg | 1 - client/public/vercel.svg | 1 - client/src/app/auth/login.tsx | 0 client/src/app/components/navbar.tsx | 0 client/src/app/globals.css | 46 - client/src/app/layout.tsx | 22 - client/src/app/lib/supabaseClient.test.ts | 30 - client/src/app/lib/supabaseClient.ts | 17 - client/src/app/middleware/requireAuth.test.ts | 76 - client/src/app/middleware/requireAuth.ts | 19 - client/src/app/page.tsx | 119 - client/src/app/register/page.tsx | 0 client/src/app/types/next.d.ts | 13 - client/tailwind.config.mjs | 20 - {client => src}/.eslintrc | 1 + {client => src}/.gitignore | 0 {client => src}/.prettierrc | 0 {client => src}/LAYOUT.md | 0 .../(ui)/(manager)}/change-access/page.tsx | 0 .../app/(ui)/(manager)}/export/page.tsx | 0 .../app/(ui)/(manager)}/manage/layout.tsx | 0 .../app/(ui)/(manager)}/manage/page.tsx | 0 .../app/(ui)/(manager)}/page.tsx | 0 src/app/(ui)/(volunteer)/checkin/page.tsx | 72 + src/app/(ui)/(volunteer)/layout.tsx | 14 + .../app/(ui)/(volunteer)/register}/page.tsx | 0 {client/src/app => src/app/(ui)}/favicon.ico | Bin src/app/(ui)/globals.css | 66 + src/app/(ui)/layout.tsx | 52 + src/app/(ui)/page.tsx | 3 + .../app}/api/checkin/route.test.ts | 0 .../app/auth => src/app}/api/checkin/route.ts | 7 +- .../app/api/checkout}/route.test.ts | 0 .../app/api/checkout}/route.ts | 7 +- .../auth => src/app}/api/login/route.test.ts | 0 .../app/auth => src/app}/api/login/route.ts | 6 +- .../auth => src/app}/api/logout/route.test.ts | 0 .../app/auth => src/app}/api/logout/route.ts | 6 +- .../app}/api/register/route.test.ts | 0 .../auth => src/app}/api/register/route.ts | 6 +- src/app/components/Dropdown.tsx | 115 + src/app/components/FormInput.tsx | 17 + src/app/components/layouts/NavBar.tsx | 32 + src/app/components/layouts/TimeDisplay.tsx | 62 + src/app/lib/supabase.test.ts | 12 + src/app/lib/supabase.ts | 7 + src/next.config.mjs | 2 + {client => src}/package-lock.json | 3190 +++++++++++++---- {client => src}/package.json | 31 +- src/public/font/PPSupplyMono-Bold.woff | Bin 0 -> 20368 bytes src/public/font/PPSupplyMono-Medium.woff | Bin 0 -> 20040 bytes src/public/font/PPSupplyMono-Regular.woff | Bin 0 -> 19584 bytes src/public/font/PPSupplyMono-Ultralight.woff | Bin 0 -> 20036 bytes src/tailwind.config.mjs | 21 + {client => src}/tsconfig.json | 5 +- 61 files changed, 2981 insertions(+), 1161 deletions(-) delete mode 100644 client/README.md delete mode 100644 client/next.config.mjs delete mode 100644 client/public/fonts/PPSupplyMono-Regular.otf delete mode 100644 client/public/fonts/PPSupplyMono-Ultralight.otf delete mode 100644 client/public/next.svg delete mode 100644 client/public/vercel.svg delete mode 100644 client/src/app/auth/login.tsx delete mode 100644 client/src/app/components/navbar.tsx delete mode 100644 client/src/app/globals.css delete mode 100644 client/src/app/layout.tsx delete mode 100644 client/src/app/lib/supabaseClient.test.ts delete mode 100644 client/src/app/lib/supabaseClient.ts delete mode 100644 client/src/app/middleware/requireAuth.test.ts delete mode 100644 client/src/app/middleware/requireAuth.ts delete mode 100644 client/src/app/page.tsx delete mode 100644 client/src/app/register/page.tsx delete mode 100644 client/src/app/types/next.d.ts delete mode 100644 client/tailwind.config.mjs rename {client => src}/.eslintrc (88%) rename {client => src}/.gitignore (100%) rename {client => src}/.prettierrc (100%) rename {client => src}/LAYOUT.md (100%) rename {client/src/app/(manage) => src/app/(ui)/(manager)}/change-access/page.tsx (100%) rename {client/src/app/(manage) => src/app/(ui)/(manager)}/export/page.tsx (100%) rename {client/src/app/(manage) => src/app/(ui)/(manager)}/manage/layout.tsx (100%) rename {client/src/app/(manage) => src/app/(ui)/(manager)}/manage/page.tsx (100%) rename {client/src/app/(manage) => src/app/(ui)/(manager)}/page.tsx (100%) create mode 100644 src/app/(ui)/(volunteer)/checkin/page.tsx create mode 100644 src/app/(ui)/(volunteer)/layout.tsx rename {client/src/app/checkin => src/app/(ui)/(volunteer)/register}/page.tsx (100%) rename {client/src/app => src/app/(ui)}/favicon.ico (100%) create mode 100644 src/app/(ui)/globals.css create mode 100644 src/app/(ui)/layout.tsx create mode 100644 src/app/(ui)/page.tsx rename {client/src/app/auth => src/app}/api/checkin/route.test.ts (100%) rename {client/src/app/auth => src/app}/api/checkin/route.ts (86%) rename {client/src/app/auth/api/checkout.ts => src/app/api/checkout}/route.test.ts (100%) rename {client/src/app/auth/api/checkout.ts => src/app/api/checkout}/route.ts (86%) rename {client/src/app/auth => src/app}/api/login/route.test.ts (100%) rename {client/src/app/auth => src/app}/api/login/route.ts (78%) rename {client/src/app/auth => src/app}/api/logout/route.test.ts (100%) rename {client/src/app/auth => src/app}/api/logout/route.ts (65%) rename {client/src/app/auth => src/app}/api/register/route.test.ts (100%) rename {client/src/app/auth => src/app}/api/register/route.ts (82%) create mode 100644 src/app/components/Dropdown.tsx create mode 100644 src/app/components/FormInput.tsx create mode 100644 src/app/components/layouts/NavBar.tsx create mode 100644 src/app/components/layouts/TimeDisplay.tsx create mode 100644 src/app/lib/supabase.test.ts create mode 100644 src/app/lib/supabase.ts create mode 100644 src/next.config.mjs rename {client => src}/package-lock.json (78%) rename {client => src}/package.json (63%) create mode 100644 src/public/font/PPSupplyMono-Bold.woff create mode 100644 src/public/font/PPSupplyMono-Medium.woff create mode 100644 src/public/font/PPSupplyMono-Regular.woff create mode 100644 src/public/font/PPSupplyMono-Ultralight.woff create mode 100644 src/tailwind.config.mjs rename {client => src}/tsconfig.json (88%) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 380fb0b..2161c83 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -17,7 +17,7 @@ jobs: defaults: run: - working-directory: ./client + working-directory: ./src steps: - name: Checkout repository @@ -28,7 +28,7 @@ jobs: with: node-version: "20" cache: "npm" - cache-dependency-path: client/package-lock.json + cache-dependency-path: src/package-lock.json - name: Install dependencies run: npm ci @@ -50,15 +50,15 @@ jobs: - name: Annotate formatting errors if: steps.format.outcome != 'success' - run: echo "::warning file=client::Formatting errors found" + run: echo "Formatting errors found" - name: Annotate linter errors if: steps.lint.outcome != 'success' - run: echo "::warning file=client::Linter errors found" + run: echo "Linter errors found" - name: Annotate test errors if: steps.test.outcome != 'success' - run: echo "::warning file=client::Test errors found" + run: echo ":Test errors found" - name: Fail the job if any checks failed if: steps.format.outcome != 'success' || steps.lint.outcome != 'success' || steps.test.outcome != 'success' diff --git a/.vscode/settings.json b/.vscode/settings.json index 879955c..9a04928 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,10 +1,8 @@ { - "eslint.workingDirectories": [ - "./client" - ], - "files.associations": { - "*.css": "tailwindcss" - }, - "explorer.sortOrder": "type", - "editor.formatOnSave": true -} \ No newline at end of file + "eslint.workingDirectories": ["./src"], + "files.associations": { + "*.css": "tailwindcss" + }, + "explorer.sortOrder": "type", + "editor.formatOnSave": true +} diff --git a/client/README.md b/client/README.md deleted file mode 100644 index 40e94a8..0000000 --- a/client/README.md +++ /dev/null @@ -1,14 +0,0 @@ -## Learn More - -To learn more about Next.js, take a look at the following resources: - -- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. -- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. - -You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome! - -## Deploy on Vercel - -The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. - -Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details. diff --git a/client/next.config.mjs b/client/next.config.mjs deleted file mode 100644 index 4678774..0000000 --- a/client/next.config.mjs +++ /dev/null @@ -1,4 +0,0 @@ -/** @type {import('next').NextConfig} */ -const nextConfig = {}; - -export default nextConfig; diff --git a/client/public/fonts/PPSupplyMono-Regular.otf b/client/public/fonts/PPSupplyMono-Regular.otf deleted file mode 100644 index dc6606554771ec9fcc321ab3312111de7d78d6fe..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 23996 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

B7es|Tvd246DySsdBuPRT} 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 \ No newline at end of file diff --git a/client/public/vercel.svg b/client/public/vercel.svg deleted file mode 100644 index d2f8422..0000000 --- a/client/public/vercel.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/client/src/app/auth/login.tsx b/client/src/app/auth/login.tsx deleted file mode 100644 index e69de29..0000000 diff --git a/client/src/app/components/navbar.tsx b/client/src/app/components/navbar.tsx deleted file mode 100644 index e69de29..0000000 diff --git a/client/src/app/globals.css b/client/src/app/globals.css deleted file mode 100644 index 2c5121f..0000000 --- a/client/src/app/globals.css +++ /dev/null @@ -1,46 +0,0 @@ -@tailwind base; -@tailwind components; -@tailwind utilities; - -/* @import url('../../public/fonts/PPSupplyMono-Ultralight.otf') */ - -@layer base { - @font-face { - font-family: "Supply Mono"; - src: url(../../public/fonts/PPSupplyMono-Ultralight.otf) format("opentype"); - } - @font-face { - font-family: "Supply Mono Regular"; - src: url(../../public/fonts/PPSupplyMono-Regular.otf) format("opentype"); - } -} - -/* :root { */ -/* --foreground-rgb: 0, 0, 0; */ -/* --background-start-rgb: 214, 219, 220; */ -/* --background-end-rgb: 255, 255, 255; */ -/* } */ -/**/ -/* @media (prefers-color-scheme: dark) { */ -/* :root { */ -/* --foreground-rgb: 255, 255, 255; */ -/* --background-start-rgb: 0, 0, 0; */ -/* --background-end-rgb: 0, 0, 0; */ -/* } */ -/* } */ -/**/ -/* body { */ -/* color: rgb(var(--foreground-rgb)); */ -/* background: linear-gradient( */ -/* to bottom, */ -/* transparent, */ -/* rgb(var(--background-end-rgb)) */ -/* ) */ -/* rgb(var(--background-start-rgb)); */ -/* } */ -/**/ -/* @layer utilities { */ -/* .text-balance { */ -/* text-wrap: balance; */ -/* } */ -/* } */ diff --git a/client/src/app/layout.tsx b/client/src/app/layout.tsx deleted file mode 100644 index a7527b0..0000000 --- a/client/src/app/layout.tsx +++ /dev/null @@ -1,22 +0,0 @@ -import type { Metadata } from "next"; -import { Inter } from "next/font/google"; -import "./globals.css"; - -const inter = Inter({ subsets: ["latin"] }); - -export const metadata: Metadata = { - title: "Create Next App", - description: "Generated by create next app" -}; - -export default function RootLayout({ - children -}: Readonly<{ - children: React.ReactNode; -}>) { - return ( - - {children} - - ); -} diff --git a/client/src/app/lib/supabaseClient.test.ts b/client/src/app/lib/supabaseClient.test.ts deleted file mode 100644 index 37c506f..0000000 --- a/client/src/app/lib/supabaseClient.test.ts +++ /dev/null @@ -1,30 +0,0 @@ -describe("Supabase Client", () => { - beforeEach(() => { - jest.resetModules(); // Clears the cache to ensure the environment variables are reset for each test - }); - - const loadSupabaseClient = async () => { - const supabaseModule = await import("./supabaseClient"); - return supabaseModule.default; - }; - - it("should create a Supabase client with the correct URL and anon key", async () => { - process.env.NEXT_PUBLIC_SUPABASE_URL = - "https://your-supabase-url.supabase.co"; - process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY = "your-supabase-anon-key"; - - const supabase = await loadSupabaseClient(); - - expect(supabase).toBeDefined(); - expect(supabase.auth).toBeDefined(); - }); - - it("should throw an error if Supabase URL or anon key is missing", async () => { - delete process.env.NEXT_PUBLIC_SUPABASE_URL; - delete process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY; - - await expect(loadSupabaseClient()).rejects.toThrow( - "Missing Supabase URL or anon key" - ); - }); -}); diff --git a/client/src/app/lib/supabaseClient.ts b/client/src/app/lib/supabaseClient.ts deleted file mode 100644 index ef11245..0000000 --- a/client/src/app/lib/supabaseClient.ts +++ /dev/null @@ -1,17 +0,0 @@ -// src/lib/supabaseClient.ts -import { createClient } from "@supabase/supabase-js"; - -const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL as string; -const supabaseAnonKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY as string; - -if (!supabaseUrl || !supabaseAnonKey) { - throw new Error("Missing Supabase URL or anon key"); -} - -const supabase = createClient(supabaseUrl, supabaseAnonKey, { - auth: { - flowType: "pkce" - } -}); - -export default supabase; diff --git a/client/src/app/middleware/requireAuth.test.ts b/client/src/app/middleware/requireAuth.test.ts deleted file mode 100644 index 61d0bba..0000000 --- a/client/src/app/middleware/requireAuth.test.ts +++ /dev/null @@ -1,76 +0,0 @@ -import { NextApiRequest, NextApiResponse } from "next"; -import supabase from "../lib/supabaseClient"; -import requireAuth from "./requireAuth"; - -jest.mock("../lib/supabaseClient", () => ({ - __esModule: true, - default: { - auth: { - getUser: jest.fn() - } - } -})); - -describe("requireAuth middleware", () => { - const mockReq = (headers = {}) => ({ headers }) as unknown as NextApiRequest; - const mockRes = () => { - const res = { - status: jest.fn().mockReturnThis(), - json: jest.fn().mockReturnThis() - }; - return res as unknown as NextApiResponse; - }; - - it("should return 401 if no authorization header is present", async () => { - const handler = jest.fn((req, res) => - res.status(200).json({ message: "success" }) - ); - const req = mockReq(); - const res = mockRes(); - - (supabase.auth.getUser as jest.Mock).mockResolvedValue({ - data: { user: null }, - error: null - }); - - await requireAuth(handler)(req, res); - - expect(res.status).toHaveBeenCalledWith(401); - expect(res.json).toHaveBeenCalledWith({ error: "Unauthorized" }); - }); - - it("should return 401 if token is invalid", async () => { - const handler = jest.fn((req, res) => - res.status(200).json({ message: "success" }) - ); - const req = mockReq(); - const res = mockRes(); - - (supabase.auth.getUser as jest.Mock).mockResolvedValue({ - data: { user: null }, - error: "Invalid token" - }); - - await requireAuth(handler)(req, res); - - expect(res.status).toHaveBeenCalledWith(401); - expect(res.json).toHaveBeenCalledWith({ error: "Unauthorized" }); - }); - - it("should call handler if token is valid", async () => { - const handler = jest.fn((req, res) => - res.status(200).json({ message: "success" }) - ); - const req = mockReq(); - const res = mockRes(); - - (supabase.auth.getUser as jest.Mock).mockResolvedValue({ - data: { user: { id: "user_id" } }, - error: null - }); - - await requireAuth(handler)(req, res); - - expect(handler).toHaveBeenCalledWith(req, res); - }); -}); diff --git a/client/src/app/middleware/requireAuth.ts b/client/src/app/middleware/requireAuth.ts deleted file mode 100644 index 0fca383..0000000 --- a/client/src/app/middleware/requireAuth.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { NextApiRequest, NextApiResponse, NextApiHandler } from "next"; -import supabase from "../lib/supabaseClient"; - -const requireAuth = - (handler: NextApiHandler) => - async (req: NextApiRequest, res: NextApiResponse) => { - const { - data: { user } - } = await supabase.auth.getUser(); - - if (!user) { - return res.status(401).json({ error: "Unauthorized" }); - } - - req.user = user; - return handler(req, res); - }; - -export default requireAuth; diff --git a/client/src/app/page.tsx b/client/src/app/page.tsx deleted file mode 100644 index de6cb81..0000000 --- a/client/src/app/page.tsx +++ /dev/null @@ -1,119 +0,0 @@ -export default function Home() { - function getTime() { - const currentTime = new Date(); - const daysOfWeek = [ - "Sunday", - "Monday", - "Tuesday", - "Wednesday", - "Thursday", - "Friday", - "Saturday" - ]; - const currentDay = daysOfWeek[currentTime.getDay()]; - - // Get current month (0 = January, 1 = February, ..., 11 = December) - const monthsOfYear = [ - "January", - "February", - "March", - "April", - "May", - "June", - "July", - "August", - "September", - "October", - "November", - "December" - ]; - - const currentMonth = monthsOfYear[currentTime.getMonth()]; - let hour = currentTime.getHours(); - const minute = currentTime.getMinutes(); - const period = hour >= 12 ? "PM" : "AM"; - hour %= 12; - hour = hour || 12; - const formattedTime = `${hour.toString().padStart(2, "0")}:${minute.toString().padStart(2, "0")}`; - - return [ - currentDay, - currentMonth, - currentTime.getDate(), - formattedTime, - period - ]; - } - - const data = getTime(); - - return ( -

- ); -} diff --git a/client/src/app/register/page.tsx b/client/src/app/register/page.tsx deleted file mode 100644 index e69de29..0000000 diff --git a/client/src/app/types/next.d.ts b/client/src/app/types/next.d.ts deleted file mode 100644 index 5a6f173..0000000 --- a/client/src/app/types/next.d.ts +++ /dev/null @@ -1,13 +0,0 @@ -// src/types/next.d.ts - -// Importing the User type from @supabase/supabase-js -import { User } from "@supabase/supabase-js"; - -// Declaring a module augmentation for 'next' -declare module "next" { - // Extending the NextApiRequest interface - interface NextApiRequest { - // Adding an optional user property of type User - user?: User; - } -} diff --git a/client/tailwind.config.mjs b/client/tailwind.config.mjs deleted file mode 100644 index 0c51c03..0000000 --- a/client/tailwind.config.mjs +++ /dev/null @@ -1,20 +0,0 @@ -export default { - content: [ - "./src/components/**/*.{js,ts,jsx,tsx,mdx}", - "./src/app/**/*.{js,ts,jsx,tsx,mdx}" - ], - theme: { - extend: { - backgroundImage: { - "gradient-radial": "radial-gradient(var(--tw-gradient-stops))", - "gradient-conic": - "conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))" - }, - fontFamily: { - supplyMono: ["Supply Mono", "sans-serif"], - supplyMonoRegular: ["Supply Mono Regular", "sans-serif"] - } - } - }, - plugins: [] -}; diff --git a/client/.eslintrc b/src/.eslintrc similarity index 88% rename from client/.eslintrc rename to src/.eslintrc index afe11aa..d82df7a 100644 --- a/client/.eslintrc +++ b/src/.eslintrc @@ -12,6 +12,7 @@ }, "rules": { "react/react-in-jsx-scope": "off", + "react/require-default-props": "off", "import/extensions": "off" } } diff --git a/client/.gitignore b/src/.gitignore similarity index 100% rename from client/.gitignore rename to src/.gitignore diff --git a/client/.prettierrc b/src/.prettierrc similarity index 100% rename from client/.prettierrc rename to src/.prettierrc diff --git a/client/LAYOUT.md b/src/LAYOUT.md similarity index 100% rename from client/LAYOUT.md rename to src/LAYOUT.md diff --git a/client/src/app/(manage)/change-access/page.tsx b/src/app/(ui)/(manager)/change-access/page.tsx similarity index 100% rename from client/src/app/(manage)/change-access/page.tsx rename to src/app/(ui)/(manager)/change-access/page.tsx diff --git a/client/src/app/(manage)/export/page.tsx b/src/app/(ui)/(manager)/export/page.tsx similarity index 100% rename from client/src/app/(manage)/export/page.tsx rename to src/app/(ui)/(manager)/export/page.tsx diff --git a/client/src/app/(manage)/manage/layout.tsx b/src/app/(ui)/(manager)/manage/layout.tsx similarity index 100% rename from client/src/app/(manage)/manage/layout.tsx rename to src/app/(ui)/(manager)/manage/layout.tsx diff --git a/client/src/app/(manage)/manage/page.tsx b/src/app/(ui)/(manager)/manage/page.tsx similarity index 100% rename from client/src/app/(manage)/manage/page.tsx rename to src/app/(ui)/(manager)/manage/page.tsx diff --git a/client/src/app/(manage)/page.tsx b/src/app/(ui)/(manager)/page.tsx similarity index 100% rename from client/src/app/(manage)/page.tsx rename to src/app/(ui)/(manager)/page.tsx diff --git a/src/app/(ui)/(volunteer)/checkin/page.tsx b/src/app/(ui)/(volunteer)/checkin/page.tsx new file mode 100644 index 0000000..a656d54 --- /dev/null +++ b/src/app/(ui)/(volunteer)/checkin/page.tsx @@ -0,0 +1,72 @@ +"use client"; + +import { useState } from "react"; +import FormInput from "@/components/FormInput"; +import Dropdown from "@/components/Dropdown"; + +function ShiftSelect() { + const [selectedShift, setSelectedShift] = useState(null); + return ( + +
+ + + setSelectedShift("dropdown")} + placeholder="SELECT" + /> +
+
+ ); +} + +export default function Checkin() { + return ( +
+ + + + + ); +} + +/* Shift Type +
+ {" "} +

ShiftName

+
+ + +
+ +
+
+
*/ diff --git a/src/app/(ui)/(volunteer)/layout.tsx b/src/app/(ui)/(volunteer)/layout.tsx new file mode 100644 index 0000000..4845239 --- /dev/null +++ b/src/app/(ui)/(volunteer)/layout.tsx @@ -0,0 +1,14 @@ +import TimeDisplay from "@/components/layouts/TimeDisplay"; + +export default function VolunteerLayout({ + children // will be a page or nested layout +}: { + children: React.ReactNode; +}) { + return ( +
+ + {children} +
+ ); +} diff --git a/client/src/app/checkin/page.tsx b/src/app/(ui)/(volunteer)/register/page.tsx similarity index 100% rename from client/src/app/checkin/page.tsx rename to src/app/(ui)/(volunteer)/register/page.tsx diff --git a/client/src/app/favicon.ico b/src/app/(ui)/favicon.ico similarity index 100% rename from client/src/app/favicon.ico rename to src/app/(ui)/favicon.ico diff --git a/src/app/(ui)/globals.css b/src/app/(ui)/globals.css new file mode 100644 index 0000000..6c56954 --- /dev/null +++ b/src/app/(ui)/globals.css @@ -0,0 +1,66 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; + +@layer base { + body { + @apply text-pedals-black; + } + + h1, + h2 { + @apply font-mono font-extralight tracking-tight; + } + + h1 { + @apply text-[64px]; + } + + h2 { + @apply text-2xl; + } + + h3 { + @apply font-inter text-2xl tracking-tight; + } + + p, + label { + @apply font-inter text-lg; + } + + label { + @apply text-pedals-darkgrey; + } + + button, + a, + input, + ::placeholder { + @apply font-mono tracking-wide; + } + + a { + @apply text-base hover:font-bold; + } + + button { + @apply text-lg; + } + + button[type="submit"] { + @apply rounded-[30px] bg-white px-6 py-2.5 hover:bg-pedals-grey; + } + + button[type="button"] { + @apply rounded-[3px] bg-white px-3 py-1.5 hover:bg-pedals-grey; + } + + input[type="text"] { + @apply flex items-center justify-start rounded-[3px] px-3 py-2 outline-none focus:ring-2 focus:ring-pedals-yellow focus:ring-offset-1; + } + + ::placeholder { + @apply text-pedals-stroke; + } +} diff --git a/src/app/(ui)/layout.tsx b/src/app/(ui)/layout.tsx new file mode 100644 index 0000000..9179151 --- /dev/null +++ b/src/app/(ui)/layout.tsx @@ -0,0 +1,52 @@ +import type { Metadata } from "next"; +import { Inter } from "next/font/google"; +import localFont from "next/font/local"; +import "./globals.css"; +import NavBar from "@/components/layouts/NavBar"; + +const inter = Inter({ + weight: ["400"], + subsets: ["latin"], + variable: "--font-inter" +}); +const supply = localFont({ + src: [ + { + path: "../../public/font/PPSupplyMono-Ultralight.woff", + weight: "200" + }, + { + path: "../../public/font/PPSupplyMono-Regular.woff", + weight: "400" + }, + { + path: "../../public/font/PPSupplyMono-Medium.woff", + weight: "500" + }, + { + path: "../../public/font/PPSupplyMono-Bold.woff", + weight: "700" + } + ], + variable: "--font-supply" +}); + +export const metadata: Metadata = { + title: "PEDALS", + description: "Volunteer Management System" +}; + +export default function RootLayout({ + children +}: Readonly<{ + children: React.ReactNode; +}>) { + return ( + + + + {children} + + + ); +} diff --git a/src/app/(ui)/page.tsx b/src/app/(ui)/page.tsx new file mode 100644 index 0000000..545694f --- /dev/null +++ b/src/app/(ui)/page.tsx @@ -0,0 +1,3 @@ +export default function Home() { + return
; +} diff --git a/client/src/app/auth/api/checkin/route.test.ts b/src/app/api/checkin/route.test.ts similarity index 100% rename from client/src/app/auth/api/checkin/route.test.ts rename to src/app/api/checkin/route.test.ts diff --git a/client/src/app/auth/api/checkin/route.ts b/src/app/api/checkin/route.ts similarity index 86% rename from client/src/app/auth/api/checkin/route.ts rename to src/app/api/checkin/route.ts index c049a32..60c41cb 100644 --- a/client/src/app/auth/api/checkin/route.ts +++ b/src/app/api/checkin/route.ts @@ -1,10 +1,5 @@ import { NextResponse } from "next/server"; -import { createClient } from "@supabase/supabase-js"; - -const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL as string; -const supabaseServiceRoleKey = process.env.SUPABASE_SERVICE_ROLE_KEY as string; - -const supabase = createClient(supabaseUrl, supabaseServiceRoleKey); +import supabase from "@/lib/supabase"; export default async function POST(req: Request) { const body = await req.json(); diff --git a/client/src/app/auth/api/checkout.ts/route.test.ts b/src/app/api/checkout/route.test.ts similarity index 100% rename from client/src/app/auth/api/checkout.ts/route.test.ts rename to src/app/api/checkout/route.test.ts diff --git a/client/src/app/auth/api/checkout.ts/route.ts b/src/app/api/checkout/route.ts similarity index 86% rename from client/src/app/auth/api/checkout.ts/route.ts rename to src/app/api/checkout/route.ts index fc8ad59..15fcd84 100644 --- a/client/src/app/auth/api/checkout.ts/route.ts +++ b/src/app/api/checkout/route.ts @@ -1,10 +1,5 @@ import { NextResponse } from "next/server"; -import { createClient } from "@supabase/supabase-js"; - -const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL as string; -const supabaseServiceRoleKey = process.env.SUPABASE_SERVICE_ROLE_KEY as string; - -const supabase = createClient(supabaseUrl, supabaseServiceRoleKey); +import supabase from "@/lib/supabase"; export default async function POST(req: Request) { const body = await req.json(); diff --git a/client/src/app/auth/api/login/route.test.ts b/src/app/api/login/route.test.ts similarity index 100% rename from client/src/app/auth/api/login/route.test.ts rename to src/app/api/login/route.test.ts diff --git a/client/src/app/auth/api/login/route.ts b/src/app/api/login/route.ts similarity index 78% rename from client/src/app/auth/api/login/route.ts rename to src/app/api/login/route.ts index 1a25f69..d3f8de7 100644 --- a/client/src/app/auth/api/login/route.ts +++ b/src/app/api/login/route.ts @@ -1,9 +1,5 @@ import { NextResponse } from "next/server"; -import { createClient } from "@supabase/supabase-js"; - -const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL as string; -const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_KEY as string; -const supabase = createClient(supabaseUrl, supabaseKey); +import supabase from "@/lib/supabase"; export default async function GET(req: Request) { const url = new URL(req.url); diff --git a/client/src/app/auth/api/logout/route.test.ts b/src/app/api/logout/route.test.ts similarity index 100% rename from client/src/app/auth/api/logout/route.test.ts rename to src/app/api/logout/route.test.ts diff --git a/client/src/app/auth/api/logout/route.ts b/src/app/api/logout/route.ts similarity index 65% rename from client/src/app/auth/api/logout/route.ts rename to src/app/api/logout/route.ts index bc2698f..db68d39 100644 --- a/client/src/app/auth/api/logout/route.ts +++ b/src/app/api/logout/route.ts @@ -1,9 +1,5 @@ import { NextResponse } from "next/server"; -import { createClient } from "@supabase/supabase-js"; - -const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL as string; -const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_KEY as string; -const supabase = createClient(supabaseUrl, supabaseKey); +import supabase from "@/lib/supabase"; export default async function POST() { try { diff --git a/client/src/app/auth/api/register/route.test.ts b/src/app/api/register/route.test.ts similarity index 100% rename from client/src/app/auth/api/register/route.test.ts rename to src/app/api/register/route.test.ts diff --git a/client/src/app/auth/api/register/route.ts b/src/app/api/register/route.ts similarity index 82% rename from client/src/app/auth/api/register/route.ts rename to src/app/api/register/route.ts index 5d676cf..ab07de1 100644 --- a/client/src/app/auth/api/register/route.ts +++ b/src/app/api/register/route.ts @@ -1,9 +1,5 @@ import { NextResponse } from "next/server"; -import { createClient } from "@supabase/supabase-js"; - -const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL as string; -const supabaseServiceRoleKey = process.env.SUPABASE_SERVICE_ROLE_KEY as string; -const supabase = createClient(supabaseUrl, supabaseServiceRoleKey); +import supabase from "@/lib/supabase"; export default async function POST(req: Request) { const body = await req.json(); diff --git a/src/app/components/Dropdown.tsx b/src/app/components/Dropdown.tsx new file mode 100644 index 0000000..f66756a --- /dev/null +++ b/src/app/components/Dropdown.tsx @@ -0,0 +1,115 @@ +"use client"; + +import React, { + useState, + useEffect, + useRef, + ComponentPropsWithoutRef +} from "react"; + +type Option = { + label: string; + value: string | number; +}; + +interface DropdownProps extends ComponentPropsWithoutRef<"div"> { + options: Option[]; + placeholder: string; +} + +export default function Dropdown({ + options, + placeholder, + ...props +}: DropdownProps) { + const [isOpen, setIsOpen] = useState(false); + const [selectedOption, setSelectedOption] = useState