From 6ed169a6e0cf06836d3eca7cbb084547fdb6e400 Mon Sep 17 00:00:00 2001 From: Kezia Date: Fri, 30 May 2025 23:13:49 -0400 Subject: [PATCH 01/89] template app --- app/globals.css | 146 ++++++- app/layout.tsx | 35 +- app/page.tsx | 95 ++++- package-lock.json | 952 ++++++++++++++++++++++++++++++++++++++++++++- package.json | 13 +- tailwind.config.ts | 47 +++ 6 files changed, 1244 insertions(+), 44 deletions(-) diff --git a/app/globals.css b/app/globals.css index 6b717ad..403b11a 100644 --- a/app/globals.css +++ b/app/globals.css @@ -3,19 +3,145 @@ @tailwind utilities; :root { - --background: #ffffff; - --foreground: #171717; + color-scheme: light; + font-synthesis: none; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + + /* Custom Color Palette */ + --background: #001745; + --background-secondary: #020617; + --foreground: #ff0068; + --card: rgba(255, 255, 255, 0.05); + --card-foreground: rgba(255, 255, 255, 0.9); + --popover: rgba(255, 255, 255, 0.1); + --popover-foreground: rgba(255, 255, 255, 0.9); + --primary: #ff0068; + --primary-foreground: rgba(255, 255, 255, 0.95); + --secondary: #d900ff; + --secondary-foreground: rgba(255, 255, 255, 0.9); + --muted: rgba(255, 255, 255, 0.1); + --muted-foreground: rgba(255, 255, 255, 0.6); + --accent: rgba(255, 255, 255, 0.05); + --accent-foreground: #9e3737; + --destructive: oklch(.577 .245 27.325); + --destructive-foreground: oklch(.577 .245 27.325); + --border: rgba(255, 255, 255, 0.1); + --input: rgba(255, 255, 255, 0.05); + --ring: rgba(255, 255, 255, 0.2); + --chart-1: oklch(.646 .222 41.116); + --chart-2: oklch(.6 .118 184.704); + --chart-3: oklch(.398 .07 227.392); + --chart-4: oklch(.828 .189 84.429); + --chart-5: oklch(.769 .188 70.08); + --radius: .625rem; } -@media (prefers-color-scheme: dark) { - :root { - --background: #0a0a0a; - --foreground: #ededed; - } +* { + box-sizing: border-box; +} + +html, body { + margin: 0; + padding: 0; + min-height: 100vh; } body { - color: var(--foreground); - background: var(--background); - font-family: Arial, Helvetica, sans-serif; + background-color: var(--background); + color: var(--primary-foreground); + font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif; + line-height: 1.6; +} + +/* Base component styles */ +.btn-primary { + background-color: var(--primary); + color: var(--primary-foreground); + border: 1px solid var(--primary); + border-radius: var(--radius); + padding: 0.5rem 1rem; + font-weight: 500; + transition: all 0.2s ease; +} + +.btn-primary:hover { + opacity: 0.9; + transform: translateY(-1px); +} + +.btn-secondary { + background-color: var(--secondary); + color: var(--secondary-foreground); + border: 1px solid var(--secondary); + border-radius: var(--radius); + padding: 0.5rem 1rem; + font-weight: 500; + transition: all 0.2s ease; +} + +.btn-secondary:hover { + opacity: 0.9; + transform: translateY(-1px); +} + +.btn-destructive { + background-color: var(--destructive); + color: var(--destructive-foreground); + border: 1px solid var(--destructive); + border-radius: var(--radius); + padding: 0.5rem 1rem; + font-weight: 500; + transition: all 0.2s ease; +} + +.btn-destructive:hover { + opacity: 0.9; + transform: translateY(-1px); +} + +.card { + background-color: var(--card); + backdrop-filter: blur(10px); + -webkit-backdrop-filter: blur(10px); + color: var(--card-foreground); + border: 1px solid var(--border); + border-radius: var(--radius); + box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2); +} + +.card-highlight { + background-color: rgba(255, 255, 255, 0.08); + backdrop-filter: blur(15px); + -webkit-backdrop-filter: blur(15px); + border: 1px solid rgba(255, 255, 255, 0.15); + border-radius: var(--radius); + box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3); +} + +.input { + background-color: var(--input); + backdrop-filter: blur(10px); + -webkit-backdrop-filter: blur(10px); + color: var(--card-foreground); + border: 1px solid var(--border); + border-radius: var(--radius); + padding: 0.5rem; + outline: none; + transition: border-color 0.2s ease; +} + +.input:focus { + border-color: var(--ring); + box-shadow: 0 0 0 2px var(--ring); +} + +.glass-panel { + background: rgba(255, 255, 255, 0.05); + backdrop-filter: blur(20px); + -webkit-backdrop-filter: blur(20px); + border: 1px solid rgba(255, 255, 255, 0.1); + border-radius: var(--radius); + box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2); } diff --git a/app/layout.tsx b/app/layout.tsx index f7fa87e..c89692d 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -1,20 +1,18 @@ import type { Metadata } from "next"; -import { Geist, Geist_Mono } from "next/font/google"; +import { Inter } from "next/font/google"; import "./globals.css"; +import { AuthProvider } from "@/contexts/AuthContext"; +import ProtectedRoute from "../components/ProtectedRoute"; +import Sidebar from "@/components/Sidebar"; -const geistSans = Geist({ - variable: "--font-geist-sans", - subsets: ["latin"], -}); - -const geistMono = Geist_Mono({ - variable: "--font-geist-mono", +const inter = Inter({ subsets: ["latin"], + variable: "--font-inter", }); export const metadata: Metadata = { - title: "Create Next App", - description: "Generated by create next app", + title: "Garuda Hacks Admin", + description: "Admin panel for Garuda Hacks", }; export default function RootLayout({ @@ -23,11 +21,18 @@ export default function RootLayout({ children: React.ReactNode; }>) { return ( - - - {children} + + + + +
+ +
+
{children}
+
+
+
+
); diff --git a/app/page.tsx b/app/page.tsx index b46616e..6303bb6 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -1,11 +1,96 @@ -import Image from "next/image"; +import PageHeader from "@/components/PageHeader"; export default function Home() { return ( -
-
- Garuda Hacks Admin -
+
+ + + {/* Applications Announcement */} +
+

+ Applications are open! +

+ +
+
+
+

+ Apply by June 19, 2025{" "} + for a spot at Garuda Hacks 6.0. +

+

+ Date: July 23, 2025 - + July 25, 2025. +

+

+ Venue: Universitas + Multimedia Nusantara. +

+
+ + +
+
+
+ + {/* Additional Announcements */} +
+
+

+ Recent Updates +

+
+
+
+
+

Workshop schedule updated

+

2 hours ago

+
+
+
+
+
+

+ New mentor applications received +

+

5 hours ago

+
+
+
+
+
+

Judging criteria finalized

+

1 day ago

+
+
+
+
+ +
+

Quick Stats

+
+
+ Total Applications + 150 +
+
+ Active Mentors + 12 +
+
+ Projects Submitted + + 25 + +
+
+
+
); } diff --git a/package-lock.json b/package-lock.json index 91b6212..2f3a8bf 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,6 +8,7 @@ "name": "gh-admin", "version": "0.1.0", "dependencies": { + "firebase": "^11.8.1", "next": "15.1.6", "react": "^19.0.0", "react-dom": "^19.0.0" @@ -164,6 +165,599 @@ "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, + "node_modules/@firebase/ai": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@firebase/ai/-/ai-1.3.0.tgz", + "integrity": "sha512-qBxJTtl9hpgZr050kVFTRADX6I0Ss6mEQyp/JEkBgKwwxixKnaRNqEDGFba4OKNL7K8E4Y7LlA/ZW6L8aCKH4A==", + "dependencies": { + "@firebase/app-check-interop-types": "0.3.3", + "@firebase/component": "0.6.17", + "@firebase/logger": "0.4.4", + "@firebase/util": "1.12.0", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "@firebase/app": "0.x", + "@firebase/app-types": "0.x" + } + }, + "node_modules/@firebase/analytics": { + "version": "0.10.16", + "resolved": "https://registry.npmjs.org/@firebase/analytics/-/analytics-0.10.16.tgz", + "integrity": "sha512-cMtp19He7Fd6uaj/nDEul+8JwvJsN8aRSJyuA1QN3QrKvfDDp+efjVurJO61sJpkVftw9O9nNMdhFbRcTmTfRQ==", + "dependencies": { + "@firebase/component": "0.6.17", + "@firebase/installations": "0.6.17", + "@firebase/logger": "0.4.4", + "@firebase/util": "1.12.0", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/analytics-compat": { + "version": "0.2.22", + "resolved": "https://registry.npmjs.org/@firebase/analytics-compat/-/analytics-compat-0.2.22.tgz", + "integrity": "sha512-VogWHgwkdYhjWKh8O1XU04uPrRaiDihkWvE/EMMmtWtaUtVALnpLnUurc3QtSKdPnvTz5uaIGKlW84DGtSPFbw==", + "dependencies": { + "@firebase/analytics": "0.10.16", + "@firebase/analytics-types": "0.8.3", + "@firebase/component": "0.6.17", + "@firebase/util": "1.12.0", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/analytics-types": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/@firebase/analytics-types/-/analytics-types-0.8.3.tgz", + "integrity": "sha512-VrIp/d8iq2g501qO46uGz3hjbDb8xzYMrbu8Tp0ovzIzrvJZ2fvmj649gTjge/b7cCCcjT0H37g1gVtlNhnkbg==" + }, + "node_modules/@firebase/app": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@firebase/app/-/app-0.13.0.tgz", + "integrity": "sha512-Vj3MST245nq+V5UmmfEkB3isIgPouyUr8yGJlFeL9Trg/umG5ogAvrjAYvQ8gV7daKDoQSRnJKWI2JFpQqRsuQ==", + "dependencies": { + "@firebase/component": "0.6.17", + "@firebase/logger": "0.4.4", + "@firebase/util": "1.12.0", + "idb": "7.1.1", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@firebase/app-check": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/@firebase/app-check/-/app-check-0.10.0.tgz", + "integrity": "sha512-AZlRlVWKcu8BH4Yf8B5EI8sOi2UNGTS8oMuthV45tbt6OVUTSQwFPIEboZzhNJNKY+fPsg7hH8vixUWFZ3lrhw==", + "dependencies": { + "@firebase/component": "0.6.17", + "@firebase/logger": "0.4.4", + "@firebase/util": "1.12.0", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/app-check-compat": { + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@firebase/app-check-compat/-/app-check-compat-0.3.25.tgz", + "integrity": "sha512-3zrsPZWAKfV7DVC20T2dgfjzjtQnSJS65OfMOiddMUtJL1S5i0nAZKsdX0bOEvvrd0SBIL8jYnfpfDeQRnhV3w==", + "dependencies": { + "@firebase/app-check": "0.10.0", + "@firebase/app-check-types": "0.5.3", + "@firebase/component": "0.6.17", + "@firebase/logger": "0.4.4", + "@firebase/util": "1.12.0", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/app-check-interop-types": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@firebase/app-check-interop-types/-/app-check-interop-types-0.3.3.tgz", + "integrity": "sha512-gAlxfPLT2j8bTI/qfe3ahl2I2YcBQ8cFIBdhAQA4I2f3TndcO+22YizyGYuttLHPQEpWkhmpFW60VCFEPg4g5A==" + }, + "node_modules/@firebase/app-check-types": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/@firebase/app-check-types/-/app-check-types-0.5.3.tgz", + "integrity": "sha512-hyl5rKSj0QmwPdsAxrI5x1otDlByQ7bvNvVt8G/XPO2CSwE++rmSVf3VEhaeOR4J8ZFaF0Z0NDSmLejPweZ3ng==" + }, + "node_modules/@firebase/app-compat": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@firebase/app-compat/-/app-compat-0.4.0.tgz", + "integrity": "sha512-LjLUrzbUgTa/sCtPoLKT2C7KShvLVHS3crnU1Du02YxnGVLE0CUBGY/NxgfR/Zg84mEbj1q08/dgesojxjn0dA==", + "dependencies": { + "@firebase/app": "0.13.0", + "@firebase/component": "0.6.17", + "@firebase/logger": "0.4.4", + "@firebase/util": "1.12.0", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@firebase/app-types": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/@firebase/app-types/-/app-types-0.9.3.tgz", + "integrity": "sha512-kRVpIl4vVGJ4baogMDINbyrIOtOxqhkZQg4jTq3l8Lw6WSk0xfpEYzezFu+Kl4ve4fbPl79dvwRtaFqAC/ucCw==" + }, + "node_modules/@firebase/auth": { + "version": "1.10.6", + "resolved": "https://registry.npmjs.org/@firebase/auth/-/auth-1.10.6.tgz", + "integrity": "sha512-cFbo2FymQltog4atI9cKTO6CxKxS0dOMXslTQrlNZRH7qhDG44/d7QeI6GXLweFZtrnlecf52ESnNz1DU6ek8w==", + "dependencies": { + "@firebase/component": "0.6.17", + "@firebase/logger": "0.4.4", + "@firebase/util": "1.12.0", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "@firebase/app": "0.x", + "@react-native-async-storage/async-storage": "^1.18.1" + }, + "peerDependenciesMeta": { + "@react-native-async-storage/async-storage": { + "optional": true + } + } + }, + "node_modules/@firebase/auth-compat": { + "version": "0.5.26", + "resolved": "https://registry.npmjs.org/@firebase/auth-compat/-/auth-compat-0.5.26.tgz", + "integrity": "sha512-4baB7tR0KukyGzrlD25aeO4t0ChLifwvDQXTBiVJE9WWwJEOjkZpHmoU9Iww0+Vdalsq4sZ3abp6YTNjHyB1dA==", + "dependencies": { + "@firebase/auth": "1.10.6", + "@firebase/auth-types": "0.13.0", + "@firebase/component": "0.6.17", + "@firebase/util": "1.12.0", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/auth-interop-types": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/@firebase/auth-interop-types/-/auth-interop-types-0.2.4.tgz", + "integrity": "sha512-JPgcXKCuO+CWqGDnigBtvo09HeBs5u/Ktc2GaFj2m01hLarbxthLNm7Fk8iOP1aqAtXV+fnnGj7U28xmk7IwVA==" + }, + "node_modules/@firebase/auth-types": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@firebase/auth-types/-/auth-types-0.13.0.tgz", + "integrity": "sha512-S/PuIjni0AQRLF+l9ck0YpsMOdE8GO2KU6ubmBB7P+7TJUCQDa3R1dlgYm9UzGbbePMZsp0xzB93f2b/CgxMOg==", + "peerDependencies": { + "@firebase/app-types": "0.x", + "@firebase/util": "1.x" + } + }, + "node_modules/@firebase/component": { + "version": "0.6.17", + "resolved": "https://registry.npmjs.org/@firebase/component/-/component-0.6.17.tgz", + "integrity": "sha512-M6DOg7OySrKEFS8kxA3MU5/xc37fiOpKPMz6cTsMUcsuKB6CiZxxNAvgFta8HGRgEpZbi8WjGIj6Uf+TpOhyzg==", + "dependencies": { + "@firebase/util": "1.12.0", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@firebase/data-connect": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@firebase/data-connect/-/data-connect-0.3.9.tgz", + "integrity": "sha512-B5tGEh5uQrQeH0i7RvlU8kbZrKOJUmoyxVIX4zLA8qQJIN6A7D+kfBlGXtSwbPdrvyaejcRPcbOtqsDQ9HPJKw==", + "dependencies": { + "@firebase/auth-interop-types": "0.2.4", + "@firebase/component": "0.6.17", + "@firebase/logger": "0.4.4", + "@firebase/util": "1.12.0", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/database": { + "version": "1.0.19", + "resolved": "https://registry.npmjs.org/@firebase/database/-/database-1.0.19.tgz", + "integrity": "sha512-khE+MIYK+XlIndVn/7mAQ9F1fwG5JHrGKaG72hblCC6JAlUBDd3SirICH6SMCf2PQ0iYkruTECth+cRhauacyQ==", + "dependencies": { + "@firebase/app-check-interop-types": "0.3.3", + "@firebase/auth-interop-types": "0.2.4", + "@firebase/component": "0.6.17", + "@firebase/logger": "0.4.4", + "@firebase/util": "1.12.0", + "faye-websocket": "0.11.4", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@firebase/database-compat": { + "version": "2.0.10", + "resolved": "https://registry.npmjs.org/@firebase/database-compat/-/database-compat-2.0.10.tgz", + "integrity": "sha512-3sjl6oGaDDYJw/Ny0E5bO6v+KM3KoD4Qo/sAfHGdRFmcJ4QnfxOX9RbG9+ce/evI3m64mkPr24LlmTDduqMpog==", + "dependencies": { + "@firebase/component": "0.6.17", + "@firebase/database": "1.0.19", + "@firebase/database-types": "1.0.14", + "@firebase/logger": "0.4.4", + "@firebase/util": "1.12.0", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@firebase/database-types": { + "version": "1.0.14", + "resolved": "https://registry.npmjs.org/@firebase/database-types/-/database-types-1.0.14.tgz", + "integrity": "sha512-8a0Q1GrxM0akgF0RiQHliinhmZd+UQPrxEmUv7MnQBYfVFiLtKOgs3g6ghRt/WEGJHyQNslZ+0PocIwNfoDwKw==", + "dependencies": { + "@firebase/app-types": "0.9.3", + "@firebase/util": "1.12.0" + } + }, + "node_modules/@firebase/firestore": { + "version": "4.7.16", + "resolved": "https://registry.npmjs.org/@firebase/firestore/-/firestore-4.7.16.tgz", + "integrity": "sha512-5OpvlwYVUTLEnqewOlXmtIpH8t2ISlZHDW0NDbKROM2D0ATMqFkMHdvl+/wz9zOAcb8GMQYlhCihOnVAliUbpQ==", + "dependencies": { + "@firebase/component": "0.6.17", + "@firebase/logger": "0.4.4", + "@firebase/util": "1.12.0", + "@firebase/webchannel-wrapper": "1.0.3", + "@grpc/grpc-js": "~1.9.0", + "@grpc/proto-loader": "^0.7.8", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/firestore-compat": { + "version": "0.3.51", + "resolved": "https://registry.npmjs.org/@firebase/firestore-compat/-/firestore-compat-0.3.51.tgz", + "integrity": "sha512-E5iubPhS6aAM7oSsHMx/FGBwfA2nbEHaK/hCs+MD3l3N7rHKnq4SYCGmVu/AraSJaMndZR1I37N9A/BH7aCq5A==", + "dependencies": { + "@firebase/component": "0.6.17", + "@firebase/firestore": "4.7.16", + "@firebase/firestore-types": "3.0.3", + "@firebase/util": "1.12.0", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/firestore-types": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@firebase/firestore-types/-/firestore-types-3.0.3.tgz", + "integrity": "sha512-hD2jGdiWRxB/eZWF89xcK9gF8wvENDJkzpVFb4aGkzfEaKxVRD1kjz1t1Wj8VZEp2LCB53Yx1zD8mrhQu87R6Q==", + "peerDependencies": { + "@firebase/app-types": "0.x", + "@firebase/util": "1.x" + } + }, + "node_modules/@firebase/functions": { + "version": "0.12.8", + "resolved": "https://registry.npmjs.org/@firebase/functions/-/functions-0.12.8.tgz", + "integrity": "sha512-p+ft6dQW0CJ3BLLxeDb5Hwk9ARw01kHTZjLqiUdPRzycR6w7Z75ThkegNmL6gCss3S0JEpldgvehgZ3kHybVhA==", + "dependencies": { + "@firebase/app-check-interop-types": "0.3.3", + "@firebase/auth-interop-types": "0.2.4", + "@firebase/component": "0.6.17", + "@firebase/messaging-interop-types": "0.2.3", + "@firebase/util": "1.12.0", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/functions-compat": { + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@firebase/functions-compat/-/functions-compat-0.3.25.tgz", + "integrity": "sha512-V0JKUw5W/7aznXf9BQ8LIYHCX6zVCM8Hdw7XUQ/LU1Y9TVP8WKRCnPB/qdPJ0xGjWWn7fhtwIYbgEw/syH4yTQ==", + "dependencies": { + "@firebase/component": "0.6.17", + "@firebase/functions": "0.12.8", + "@firebase/functions-types": "0.6.3", + "@firebase/util": "1.12.0", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/functions-types": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/@firebase/functions-types/-/functions-types-0.6.3.tgz", + "integrity": "sha512-EZoDKQLUHFKNx6VLipQwrSMh01A1SaL3Wg6Hpi//x6/fJ6Ee4hrAeswK99I5Ht8roiniKHw4iO0B1Oxj5I4plg==" + }, + "node_modules/@firebase/installations": { + "version": "0.6.17", + "resolved": "https://registry.npmjs.org/@firebase/installations/-/installations-0.6.17.tgz", + "integrity": "sha512-zfhqCNJZRe12KyADtRrtOj+SeSbD1H/K8J24oQAJVv/u02eQajEGlhZtcx9Qk7vhGWF5z9dvIygVDYqLL4o1XQ==", + "dependencies": { + "@firebase/component": "0.6.17", + "@firebase/util": "1.12.0", + "idb": "7.1.1", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/installations-compat": { + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/@firebase/installations-compat/-/installations-compat-0.2.17.tgz", + "integrity": "sha512-J7afeCXB7yq25FrrJAgbx8mn1nG1lZEubOLvYgG7ZHvyoOCK00sis5rj7TgDrLYJgdj/SJiGaO1BD3BAp55TeA==", + "dependencies": { + "@firebase/component": "0.6.17", + "@firebase/installations": "0.6.17", + "@firebase/installations-types": "0.5.3", + "@firebase/util": "1.12.0", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/installations-types": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/@firebase/installations-types/-/installations-types-0.5.3.tgz", + "integrity": "sha512-2FJI7gkLqIE0iYsNQ1P751lO3hER+Umykel+TkLwHj6plzWVxqvfclPUZhcKFVQObqloEBTmpi2Ozn7EkCABAA==", + "peerDependencies": { + "@firebase/app-types": "0.x" + } + }, + "node_modules/@firebase/logger": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/@firebase/logger/-/logger-0.4.4.tgz", + "integrity": "sha512-mH0PEh1zoXGnaR8gD1DeGeNZtWFKbnz9hDO91dIml3iou1gpOnLqXQ2dJfB71dj6dpmUjcQ6phY3ZZJbjErr9g==", + "dependencies": { + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@firebase/messaging": { + "version": "0.12.21", + "resolved": "https://registry.npmjs.org/@firebase/messaging/-/messaging-0.12.21.tgz", + "integrity": "sha512-bYJ2Evj167Z+lJ1ach6UglXz5dUKY1zrJZd15GagBUJSR7d9KfiM1W8dsyL0lDxcmhmA/sLaBYAAhF1uilwN0g==", + "dependencies": { + "@firebase/component": "0.6.17", + "@firebase/installations": "0.6.17", + "@firebase/messaging-interop-types": "0.2.3", + "@firebase/util": "1.12.0", + "idb": "7.1.1", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/messaging-compat": { + "version": "0.2.21", + "resolved": "https://registry.npmjs.org/@firebase/messaging-compat/-/messaging-compat-0.2.21.tgz", + "integrity": "sha512-1yMne+4BGLbHbtyu/VyXWcLiefUE1+K3ZGfVTyKM4BH4ZwDFRGoWUGhhx+tKRX4Tu9z7+8JN67SjnwacyNWK5g==", + "dependencies": { + "@firebase/component": "0.6.17", + "@firebase/messaging": "0.12.21", + "@firebase/util": "1.12.0", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/messaging-interop-types": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@firebase/messaging-interop-types/-/messaging-interop-types-0.2.3.tgz", + "integrity": "sha512-xfzFaJpzcmtDjycpDeCUj0Ge10ATFi/VHVIvEEjDNc3hodVBQADZ7BWQU7CuFpjSHE+eLuBI13z5F/9xOoGX8Q==" + }, + "node_modules/@firebase/performance": { + "version": "0.7.6", + "resolved": "https://registry.npmjs.org/@firebase/performance/-/performance-0.7.6.tgz", + "integrity": "sha512-AsOz74dSTlyQGlnnbLWXiHFAsrxhpssPOsFFi4HgOJ5DjzkK7ZdZ/E9uMPrwFoXJyMVoybGRuqsL/wkIbFITsA==", + "dependencies": { + "@firebase/component": "0.6.17", + "@firebase/installations": "0.6.17", + "@firebase/logger": "0.4.4", + "@firebase/util": "1.12.0", + "tslib": "^2.1.0", + "web-vitals": "^4.2.4" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/performance-compat": { + "version": "0.2.19", + "resolved": "https://registry.npmjs.org/@firebase/performance-compat/-/performance-compat-0.2.19.tgz", + "integrity": "sha512-4cU0T0BJ+LZK/E/UwFcvpBCVdkStgBMQwBztM9fJPT6udrEUk3ugF5/HT+E2Z22FCXtIaXDukJbYkE/c3c6IHw==", + "dependencies": { + "@firebase/component": "0.6.17", + "@firebase/logger": "0.4.4", + "@firebase/performance": "0.7.6", + "@firebase/performance-types": "0.2.3", + "@firebase/util": "1.12.0", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/performance-types": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@firebase/performance-types/-/performance-types-0.2.3.tgz", + "integrity": "sha512-IgkyTz6QZVPAq8GSkLYJvwSLr3LS9+V6vNPQr0x4YozZJiLF5jYixj0amDtATf1X0EtYHqoPO48a9ija8GocxQ==" + }, + "node_modules/@firebase/remote-config": { + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/@firebase/remote-config/-/remote-config-0.6.4.tgz", + "integrity": "sha512-ZyLJRT46wtycyz2+opEkGaoFUOqRQjt/0NX1WfUISOMCI/PuVoyDjqGpq24uK+e8D5NknyTpiXCVq5dowhScmg==", + "dependencies": { + "@firebase/component": "0.6.17", + "@firebase/installations": "0.6.17", + "@firebase/logger": "0.4.4", + "@firebase/util": "1.12.0", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/remote-config-compat": { + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/@firebase/remote-config-compat/-/remote-config-compat-0.2.17.tgz", + "integrity": "sha512-KelsBD0sXSC0u3esr/r6sJYGRN6pzn3bYuI/6pTvvmZbjBlxQkRabHAVH6d+YhLcjUXKIAYIjZszczd1QJtOyA==", + "dependencies": { + "@firebase/component": "0.6.17", + "@firebase/logger": "0.4.4", + "@firebase/remote-config": "0.6.4", + "@firebase/remote-config-types": "0.4.0", + "@firebase/util": "1.12.0", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/remote-config-types": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@firebase/remote-config-types/-/remote-config-types-0.4.0.tgz", + "integrity": "sha512-7p3mRE/ldCNYt8fmWMQ/MSGRmXYlJ15Rvs9Rk17t8p0WwZDbeK7eRmoI1tvCPaDzn9Oqh+yD6Lw+sGLsLg4kKg==" + }, + "node_modules/@firebase/storage": { + "version": "0.13.12", + "resolved": "https://registry.npmjs.org/@firebase/storage/-/storage-0.13.12.tgz", + "integrity": "sha512-5JmoFS01MYjW1XMQa5F5rD/kvMwBN10QF03bmcuJWq4lg+BJ3nRgL3sscWnyJPhwM/ZCyv2eRwcfzESVmsYkdQ==", + "dependencies": { + "@firebase/component": "0.6.17", + "@firebase/util": "1.12.0", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/storage-compat": { + "version": "0.3.22", + "resolved": "https://registry.npmjs.org/@firebase/storage-compat/-/storage-compat-0.3.22.tgz", + "integrity": "sha512-29j6JgXTjQ76sOIkxmTNHQfYA/hDTeV9qGbn0jolynPXSg/AmzCB0CpCoCYrS0ja0Flgmy1hkA3XYDZ/eiV1Cg==", + "dependencies": { + "@firebase/component": "0.6.17", + "@firebase/storage": "0.13.12", + "@firebase/storage-types": "0.8.3", + "@firebase/util": "1.12.0", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/storage-types": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/@firebase/storage-types/-/storage-types-0.8.3.tgz", + "integrity": "sha512-+Muk7g9uwngTpd8xn9OdF/D48uiQ7I1Fae7ULsWPuKoCH3HU7bfFPhxtJYzyhjdniowhuDpQcfPmuNRAqZEfvg==", + "peerDependencies": { + "@firebase/app-types": "0.x", + "@firebase/util": "1.x" + } + }, + "node_modules/@firebase/util": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/@firebase/util/-/util-1.12.0.tgz", + "integrity": "sha512-Z4rK23xBCwgKDqmzGVMef+Vb4xso2j5Q8OG0vVL4m4fA5ZjPMYQazu8OJJC3vtQRC3SQ/Pgx/6TPNVsCd70QRw==", + "hasInstallScript": true, + "dependencies": { + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@firebase/webchannel-wrapper": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@firebase/webchannel-wrapper/-/webchannel-wrapper-1.0.3.tgz", + "integrity": "sha512-2xCRM9q9FlzGZCdgDMJwc0gyUkWFtkosy7Xxr6sFgQwn+wMNIWd7xIvYNauU1r64B5L5rsGKy/n9TKJ0aAFeqQ==" + }, + "node_modules/@grpc/grpc-js": { + "version": "1.9.15", + "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.9.15.tgz", + "integrity": "sha512-nqE7Hc0AzI+euzUwDAy0aY5hCp10r734gMGRdU+qOPX0XSceI2ULrcXB5U2xSc5VkWwalCj4M7GzCAygZl2KoQ==", + "dependencies": { + "@grpc/proto-loader": "^0.7.8", + "@types/node": ">=12.12.47" + }, + "engines": { + "node": "^8.13.0 || >=10.10.0" + } + }, + "node_modules/@grpc/proto-loader": { + "version": "0.7.15", + "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.7.15.tgz", + "integrity": "sha512-tMXdRCfYVixjuFK+Hk0Q1s38gV9zDiDJfWL3h1rv4Qc39oILCu1TRTDt7+fGUI8K4G1Fj125Hx/ru3azECWTyQ==", + "dependencies": { + "lodash.camelcase": "^4.3.0", + "long": "^5.0.0", + "protobufjs": "^7.2.5", + "yargs": "^17.7.2" + }, + "bin": { + "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/@humanfs/core": { "version": "0.19.1", "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", @@ -820,6 +1414,60 @@ "node": ">=14" } }, + "node_modules/@protobufjs/aspromise": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", + "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==" + }, + "node_modules/@protobufjs/base64": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", + "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==" + }, + "node_modules/@protobufjs/codegen": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", + "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==" + }, + "node_modules/@protobufjs/eventemitter": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", + "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==" + }, + "node_modules/@protobufjs/fetch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", + "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", + "dependencies": { + "@protobufjs/aspromise": "^1.1.1", + "@protobufjs/inquire": "^1.1.0" + } + }, + "node_modules/@protobufjs/float": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", + "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==" + }, + "node_modules/@protobufjs/inquire": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", + "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==" + }, + "node_modules/@protobufjs/path": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", + "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==" + }, + "node_modules/@protobufjs/pool": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", + "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==" + }, + "node_modules/@protobufjs/utf8": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", + "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==" + }, "node_modules/@rtsao/scc": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz", @@ -867,7 +1515,6 @@ "version": "20.17.17", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.17.tgz", "integrity": "sha512-/WndGO4kIfMicEQLTi/mDANUu/iVUhT7KboZPdEqqHQ4aTS+3qT3U5gIqWDFV+XouorjfgGqvKILJeHhuQgFYg==", - "dev": true, "dependencies": { "undici-types": "~6.19.2" } @@ -1167,7 +1814,6 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, "dependencies": { "color-convert": "^2.0.1" }, @@ -1607,6 +2253,72 @@ "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==" }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/cliui/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "node_modules/cliui/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, "node_modules/color": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", @@ -1624,7 +2336,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "devOptional": true, "dependencies": { "color-name": "~1.1.4" }, @@ -1635,8 +2346,7 @@ "node_modules/color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "devOptional": true + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, "node_modules/color-string": { "version": "1.9.1", @@ -2044,6 +2754,14 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "engines": { + "node": ">=6" + } + }, "node_modules/escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", @@ -2543,6 +3261,17 @@ "reusify": "^1.0.4" } }, + "node_modules/faye-websocket": { + "version": "0.11.4", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz", + "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==", + "dependencies": { + "websocket-driver": ">=0.5.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, "node_modules/file-entry-cache": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", @@ -2583,6 +3312,41 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/firebase": { + "version": "11.8.1", + "resolved": "https://registry.npmjs.org/firebase/-/firebase-11.8.1.tgz", + "integrity": "sha512-oetXhPCvJZM4DVL/n/06442emMU+KzM0JLZjszpwlU6mqdFZqBwumBxn6hQkLukJyU5wsjihZHUY8HEAE2micg==", + "dependencies": { + "@firebase/ai": "1.3.0", + "@firebase/analytics": "0.10.16", + "@firebase/analytics-compat": "0.2.22", + "@firebase/app": "0.13.0", + "@firebase/app-check": "0.10.0", + "@firebase/app-check-compat": "0.3.25", + "@firebase/app-compat": "0.4.0", + "@firebase/app-types": "0.9.3", + "@firebase/auth": "1.10.6", + "@firebase/auth-compat": "0.5.26", + "@firebase/data-connect": "0.3.9", + "@firebase/database": "1.0.19", + "@firebase/database-compat": "2.0.10", + "@firebase/firestore": "4.7.16", + "@firebase/firestore-compat": "0.3.51", + "@firebase/functions": "0.12.8", + "@firebase/functions-compat": "0.3.25", + "@firebase/installations": "0.6.17", + "@firebase/installations-compat": "0.2.17", + "@firebase/messaging": "0.12.21", + "@firebase/messaging-compat": "0.2.21", + "@firebase/performance": "0.7.6", + "@firebase/performance-compat": "0.2.19", + "@firebase/remote-config": "0.6.4", + "@firebase/remote-config-compat": "0.2.17", + "@firebase/storage": "0.13.12", + "@firebase/storage-compat": "0.3.22", + "@firebase/util": "1.12.0" + } + }, "node_modules/flat-cache": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", @@ -2685,6 +3449,14 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, "node_modules/get-intrinsic": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.7.tgz", @@ -2946,6 +3718,16 @@ "node": ">= 0.4" } }, + "node_modules/http-parser-js": { + "version": "0.5.10", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.10.tgz", + "integrity": "sha512-Pysuw9XpUq5dVc/2SMHpuTY01RFl8fttgcyunjL7eEMhGM3cI4eOmiCycJDVCo/7O7ClfQD3SaI6ftDzqOXYMA==" + }, + "node_modules/idb": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/idb/-/idb-7.1.1.tgz", + "integrity": "sha512-gchesWBzyvGHRO9W8tzUWFDycow5gwjvFKfyV9FF32Y7F50yZMp7mP+T2mJIWFx49zicqyC4uefHM17o6xKIVQ==" + }, "node_modules/ignore": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", @@ -3176,7 +3958,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, "engines": { "node": ">=8" } @@ -3573,12 +4354,22 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/lodash.camelcase": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", + "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==" + }, "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true }, + "node_modules/long": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz", + "integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==" + }, "node_modules/loose-envify": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", @@ -4239,6 +5030,29 @@ "react-is": "^16.13.1" } }, + "node_modules/protobufjs": { + "version": "7.5.3", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.5.3.tgz", + "integrity": "sha512-sildjKwVqOI2kmFDiXQ6aEB0fjYTafpEvIBs8tOR8qI4spuL9OPROLVu2qZqi/xgCfsHIwVqlaF8JBjWFHnKbw==", + "hasInstallScript": true, + "dependencies": { + "@protobufjs/aspromise": "^1.1.2", + "@protobufjs/base64": "^1.1.2", + "@protobufjs/codegen": "^2.0.4", + "@protobufjs/eventemitter": "^1.1.0", + "@protobufjs/fetch": "^1.1.0", + "@protobufjs/float": "^1.0.2", + "@protobufjs/inquire": "^1.1.0", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.0", + "@types/node": ">=13.7.0", + "long": "^5.0.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", @@ -4356,6 +5170,14 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/resolve": { "version": "1.22.10", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", @@ -4446,6 +5268,25 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, "node_modules/safe-push-apply": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz", @@ -5271,8 +6112,7 @@ "node_modules/undici-types": { "version": "6.19.8", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", - "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", - "dev": true + "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==" }, "node_modules/uri-js": { "version": "4.4.1", @@ -5289,6 +6129,32 @@ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", "dev": true }, + "node_modules/web-vitals": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/web-vitals/-/web-vitals-4.2.4.tgz", + "integrity": "sha512-r4DIlprAGwJ7YM11VZp4R884m0Vmgr6EAKe3P+kO0PPj3Unqyvv59rczf6UiGcb9Z8QxZVcqKNwv/g0WNdWwsw==" + }, + "node_modules/websocket-driver": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", + "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==", + "dependencies": { + "http-parser-js": ">=0.5.1", + "safe-buffer": ">=5.1.0", + "websocket-extensions": ">=0.1.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/websocket-extensions": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz", + "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==", + "engines": { + "node": ">=0.8.0" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -5485,6 +6351,14 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "engines": { + "node": ">=10" + } + }, "node_modules/yaml": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.7.0.tgz", @@ -5497,6 +6371,68 @@ "node": ">= 14" } }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "node_modules/yargs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", diff --git a/package.json b/package.json index 7f22012..77f4046 100644 --- a/package.json +++ b/package.json @@ -9,19 +9,20 @@ "lint": "next lint" }, "dependencies": { + "firebase": "^11.8.1", + "next": "15.1.6", "react": "^19.0.0", - "react-dom": "^19.0.0", - "next": "15.1.6" + "react-dom": "^19.0.0" }, "devDependencies": { - "typescript": "^5", + "@eslint/eslintrc": "^3", "@types/node": "^20", "@types/react": "^19", "@types/react-dom": "^19", - "postcss": "^8", - "tailwindcss": "^3.4.1", "eslint": "^9", "eslint-config-next": "15.1.6", - "@eslint/eslintrc": "^3" + "postcss": "^8", + "tailwindcss": "^3.4.1", + "typescript": "^5" } } diff --git a/tailwind.config.ts b/tailwind.config.ts index 1362b88..654351f 100644 --- a/tailwind.config.ts +++ b/tailwind.config.ts @@ -10,7 +10,54 @@ export default { extend: { colors: { background: "var(--background)", + "background-secondary": "var(--background-secondary)", foreground: "var(--foreground)", + card: { + DEFAULT: "var(--card)", + foreground: "var(--card-foreground)", + }, + popover: { + DEFAULT: "var(--popover)", + foreground: "var(--popover-foreground)", + }, + primary: { + DEFAULT: "var(--primary)", + foreground: "var(--primary-foreground)", + }, + secondary: { + DEFAULT: "var(--secondary)", + foreground: "var(--secondary-foreground)", + }, + muted: { + DEFAULT: "var(--muted)", + foreground: "var(--muted-foreground)", + }, + accent: { + DEFAULT: "var(--accent)", + foreground: "var(--accent-foreground)", + }, + destructive: { + DEFAULT: "var(--destructive)", + foreground: "var(--destructive-foreground)", + }, + border: "var(--border)", + input: "var(--input)", + ring: "var(--ring)", + chart: { + "1": "var(--chart-1)", + "2": "var(--chart-2)", + "3": "var(--chart-3)", + "4": "var(--chart-4)", + "5": "var(--chart-5)", + }, + }, + borderRadius: { + lg: "var(--radius)", + md: "calc(var(--radius) - 2px)", + sm: "calc(var(--radius) - 4px)", + }, + fontFamily: { + sans: ["Inter", "system-ui", "Avenir", "Helvetica", "Arial", "sans-serif"], }, }, }, From bfccd3483018c5dbe79a37e085895edad97461de Mon Sep 17 00:00:00 2001 From: Kezia Date: Fri, 30 May 2025 23:14:05 -0400 Subject: [PATCH 02/89] add pages and placeholder content --- .firebaserc | 5 + .github/workflows/firebase-hosting-merge.yml | 20 ++ .../firebase-hosting-pull-request.yml | 21 ++ app/applications/page.tsx | 128 +++++++++ app/judging/page.tsx | 216 ++++++++++++++++ app/mentorship/page.tsx | 113 ++++++++ components/AuthProvider.tsx | 0 components/LoginForm.tsx | 103 ++++++++ components/PageHeader.tsx | 17 ++ components/ProtectedRoute.tsx | 50 ++++ components/Sidebar.tsx | 243 ++++++++++++++++++ contexts/AuthContext.tsx | 155 +++++++++++ firebase.json | 22 ++ firestore.indexes.json | 4 + firestore.rules | 9 + lib/firebase.ts | 29 +++ lib/firebaseUtils.ts | 0 public/404.html | 33 +++ public/index.html | 89 +++++++ 19 files changed, 1257 insertions(+) create mode 100644 .firebaserc create mode 100644 .github/workflows/firebase-hosting-merge.yml create mode 100644 .github/workflows/firebase-hosting-pull-request.yml create mode 100644 app/applications/page.tsx create mode 100644 app/judging/page.tsx create mode 100644 app/mentorship/page.tsx create mode 100644 components/AuthProvider.tsx create mode 100644 components/LoginForm.tsx create mode 100644 components/PageHeader.tsx create mode 100644 components/ProtectedRoute.tsx create mode 100644 components/Sidebar.tsx create mode 100644 contexts/AuthContext.tsx create mode 100644 firebase.json create mode 100644 firestore.indexes.json create mode 100644 firestore.rules create mode 100644 lib/firebase.ts create mode 100644 lib/firebaseUtils.ts create mode 100644 public/404.html create mode 100644 public/index.html diff --git a/.firebaserc b/.firebaserc new file mode 100644 index 0000000..f734588 --- /dev/null +++ b/.firebaserc @@ -0,0 +1,5 @@ +{ + "projects": { + "default": "garuda-hacks-6-0" + } +} diff --git a/.github/workflows/firebase-hosting-merge.yml b/.github/workflows/firebase-hosting-merge.yml new file mode 100644 index 0000000..27230a5 --- /dev/null +++ b/.github/workflows/firebase-hosting-merge.yml @@ -0,0 +1,20 @@ +# This file was auto-generated by the Firebase CLI +# https://github.com/firebase/firebase-tools + +name: Deploy to Firebase Hosting on merge +on: + push: + branches: + - main +jobs: + build_and_deploy: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - run: npm ci && npm run build + - uses: FirebaseExtended/action-hosting-deploy@v0 + with: + repoToken: ${{ secrets.GITHUB_TOKEN }} + firebaseServiceAccount: ${{ secrets.FIREBASE_SERVICE_ACCOUNT_GARUDA_HACKS_6_0 }} + channelId: live + projectId: garuda-hacks-6-0 diff --git a/.github/workflows/firebase-hosting-pull-request.yml b/.github/workflows/firebase-hosting-pull-request.yml new file mode 100644 index 0000000..86e2ff6 --- /dev/null +++ b/.github/workflows/firebase-hosting-pull-request.yml @@ -0,0 +1,21 @@ +# This file was auto-generated by the Firebase CLI +# https://github.com/firebase/firebase-tools + +name: Deploy to Firebase Hosting on PR +on: pull_request +permissions: + checks: write + contents: read + pull-requests: write +jobs: + build_and_preview: + if: ${{ github.event.pull_request.head.repo.full_name == github.repository }} + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - run: npm ci && npm run build + - uses: FirebaseExtended/action-hosting-deploy@v0 + with: + repoToken: ${{ secrets.GITHUB_TOKEN }} + firebaseServiceAccount: ${{ secrets.FIREBASE_SERVICE_ACCOUNT_GARUDA_HACKS_6_0 }} + projectId: garuda-hacks-6-0 diff --git a/app/applications/page.tsx b/app/applications/page.tsx new file mode 100644 index 0000000..cf933cd --- /dev/null +++ b/app/applications/page.tsx @@ -0,0 +1,128 @@ +import PageHeader from "@/components/PageHeader"; + +export default function Applications() { + return ( +
+ + +
+
+
+
150
+
Total Applications
+
+
+
+
+
85
+
Pending Review
+
+
+
+
+
+ 45 +
+
Approved
+
+
+
+
+
20
+
Rejected
+
+
+
+ +
+
+

+ Recent Applications +

+
+
+ + + + + + + + + + + + {[ + { + name: "John Doe", + email: "john@example.com", + university: "University of Indonesia", + status: "pending", + }, + { + name: "Jane Smith", + email: "jane@example.com", + university: "Bandung Institute of Technology", + status: "approved", + }, + { + name: "Bob Johnson", + email: "bob@example.com", + university: "Gadjah Mada University", + status: "pending", + }, + ].map((application, index) => ( + + + + + + + + ))} + +
+ Name + + Email + + University + + Status + + Actions +
{application.name}{application.email} + {application.university} + + + {application.status} + + +
+ + + +
+
+
+
+
+ ); +} diff --git a/app/judging/page.tsx b/app/judging/page.tsx new file mode 100644 index 0000000..1cb5fa1 --- /dev/null +++ b/app/judging/page.tsx @@ -0,0 +1,216 @@ +import PageHeader from "@/components/PageHeader"; + +export default function Judging() { + return ( +
+ + + {/* Judging Overview */} +
+
+
+
8
+
Active Judges
+
+
+
+
+
25
+
+ Projects Submitted +
+
+
+
+
+
+ 18 +
+
Projects Judged
+
+
+
+
+
7
+
Pending Review
+
+
+
+ + {/* Judging Progress */} +
+
+

+ Judging Progress +

+
+
+
+ Overall Progress + 72% +
+
+
+
+
+
+
+ Technical Review + 85% +
+
+
+
+
+
+
+ Design Review + 60% +
+
+
+
+
+
+
+ +
+

+ Top Projects +

+
+ {[ + { name: "EcoTrack", score: 9.2, category: "Sustainability" }, + { name: "HealthLink", score: 8.9, category: "Healthcare" }, + { name: "EduVerse", score: 8.7, category: "Education" }, + { name: "FinFlow", score: 8.5, category: "Fintech" }, + ].map((project, index) => ( +
+
+
+ {project.name} +
+
+ {project.category} +
+
+
+
{project.score}
+
/ 10
+
+
+ ))} +
+
+
+ + {/* Judges Panel */} +
+
+
+

+ Judges Panel +

+ +
+
+
+ + + + + + + + + + + + {[ + { + name: "Prof. Anderson", + expertise: "AI/ML", + assigned: 4, + completed: 4, + status: "Available", + }, + { + name: "Dr. Liu", + expertise: "Web Development", + assigned: 3, + completed: 2, + status: "Judging", + }, + { + name: "Sarah Williams", + expertise: "UI/UX", + assigned: 3, + completed: 3, + status: "Available", + }, + { + name: "Mark Thompson", + expertise: "Mobile Dev", + assigned: 4, + completed: 1, + status: "Judging", + }, + ].map((judge, index) => ( + + + + + + + + ))} + +
+ Judge + + Expertise + + Projects Assigned + + Completed + + Status +
+ {judge.name} + + {judge.expertise} + + {judge.assigned} + + {judge.completed} + + + {judge.status} + +
+
+
+
+ ); +} diff --git a/app/mentorship/page.tsx b/app/mentorship/page.tsx new file mode 100644 index 0000000..a76c098 --- /dev/null +++ b/app/mentorship/page.tsx @@ -0,0 +1,113 @@ +import PageHeader from "@/components/PageHeader"; + +export default function Mentorship() { + return ( +
+ + +
+
+
+
12
+
Active Mentors
+
+
+
+
+
45
+
Teams Matched
+
+
+
+
+
+ 28 +
+
Sessions Completed
+
+
+
+ +
+
+
+

+ Available Mentors +

+ +
+
+
+
+ {[ + { + name: "Dr. Sarah Chen", + expertise: "AI/ML, Data Science", + teams: 3, + rating: 4.9, + }, + { + name: "Alex Rodriguez", + expertise: "Full Stack Development", + teams: 4, + rating: 4.8, + }, + { + name: "Maya Patel", + expertise: "UI/UX Design", + teams: 2, + rating: 5.0, + }, + { + name: "James Kim", + expertise: "Blockchain, Web3", + teams: 3, + rating: 4.7, + }, + { + name: "Emily Johnson", + expertise: "Mobile Development", + teams: 2, + rating: 4.9, + }, + { + name: "Roberto Silva", + expertise: "DevOps, Cloud", + teams: 1, + rating: 4.6, + }, + ].map((mentor, index) => ( +
+
+
+

{mentor.name}

+

{mentor.expertise}

+
+
+
+ ★ {mentor.rating} +
+
+
+
+ + {mentor.teams} teams assigned + + +
+
+ ))} +
+
+
+
+ ); +} diff --git a/components/AuthProvider.tsx b/components/AuthProvider.tsx new file mode 100644 index 0000000..e69de29 diff --git a/components/LoginForm.tsx b/components/LoginForm.tsx new file mode 100644 index 0000000..03a09ce --- /dev/null +++ b/components/LoginForm.tsx @@ -0,0 +1,103 @@ +"use client"; + +import { useState } from "react"; +import { useAuth } from "@/contexts/AuthContext"; + +export default function LoginForm() { + const [error, setError] = useState(""); + const [loading, setLoading] = useState(false); + + const { signInWithGoogle } = useAuth(); + + const handleGoogleSignIn = async () => { + setError(""); + setLoading(true); + + try { + await signInWithGoogle(); + } catch (error: any) { + setError(error.message); + } finally { + setLoading(false); + } + }; + + return ( +
+
+
+

+ Garuda Hacks Admin +

+

+ Sign in with your @garudahacks.com Google account +

+
+ +
+ {error && ( +
+ {error} +
+ )} + +
+ +
+ +
+

This will only work with @garudahacks.com accounts

+

Make sure you're signed into the correct Google account

+
+
+
+
+ ); +} diff --git a/components/PageHeader.tsx b/components/PageHeader.tsx new file mode 100644 index 0000000..1293f3c --- /dev/null +++ b/components/PageHeader.tsx @@ -0,0 +1,17 @@ +interface PageHeaderProps { + title: string; + subtitle: string; + } + + export default function PageHeader({ title, subtitle }: PageHeaderProps) { + return ( +
+

+ {title} +

+

+ {subtitle} +

+
+ ); + } \ No newline at end of file diff --git a/components/ProtectedRoute.tsx b/components/ProtectedRoute.tsx new file mode 100644 index 0000000..a2f9880 --- /dev/null +++ b/components/ProtectedRoute.tsx @@ -0,0 +1,50 @@ +"use client"; + +import { useAuth } from "@/contexts/AuthContext"; +import LoginForm from "./LoginForm"; + +interface ProtectedRouteProps { + children: React.ReactNode; +} + +export default function ProtectedRoute({ children }: ProtectedRouteProps) { + const { user, loading } = useAuth(); + + if (loading) { + return ( +
+
+ + + + +
+ Loading... +
+
+
+ ); + } + + if (!user) { + return ; + } + + return <>{children}; +} diff --git a/components/Sidebar.tsx b/components/Sidebar.tsx new file mode 100644 index 0000000..a5b7e30 --- /dev/null +++ b/components/Sidebar.tsx @@ -0,0 +1,243 @@ +"use client"; + +import { useAuth } from "@/contexts/AuthContext"; +import { usePathname } from "next/navigation"; +import Link from "next/link"; +import { useState } from "react"; + +export default function Sidebar() { + const { user, signOut } = useAuth(); + const pathname = usePathname(); + const [showProfileMenu, setShowProfileMenu] = useState(false); + + const handleSignOut = async () => { + try { + await signOut(); + } catch (error) { + console.error("Error signing out:", error); + } + }; + + const navigation = [ + { + name: "Home", + href: "/", + icon: ( + + + + ), + }, + { + name: "Applications", + href: "/applications", + icon: ( + + + + ), + }, + { + name: "Mentorship", + href: "/mentorship", + icon: ( + + + + ), + }, + { + name: "Judging", + href: "/judging", + icon: ( + + + + ), + }, + ]; + + return ( +
+
+
+
+ + GH + +
+
+

+ Garuda Hacks +

+

Admin Panel

+
+
+
+ + + +
+
+ + + {showProfileMenu && ( +
+
+

+ {user?.displayName || "User"} +

+

{user?.email}

+
+ + + + +
+ )} +
+
+
+ ); +} diff --git a/contexts/AuthContext.tsx b/contexts/AuthContext.tsx new file mode 100644 index 0000000..953f0e5 --- /dev/null +++ b/contexts/AuthContext.tsx @@ -0,0 +1,155 @@ +"use client"; + +import { + createContext, + useContext, + useEffect, + useState, + ReactNode, +} from "react"; +import { + User, + signInWithEmailAndPassword, + signOut as firebaseSignOut, + onAuthStateChanged, + sendPasswordResetEmail, + signInWithPopup, + GoogleAuthProvider, +} from "firebase/auth"; +import { auth, googleProvider } from "@/lib/firebase"; + +interface AuthContextType { + user: User | null; + loading: boolean; + signIn: (email: string, password: string) => Promise; + signOut: () => Promise; + resetPassword: (email: string) => Promise; + signInWithGoogle: () => Promise; +} + +const AuthContext = createContext(undefined); + +export const useAuth = () => { + const context = useContext(AuthContext); + if (context === undefined) { + throw new Error("useAuth must be used within an AuthProvider"); + } + return context; +}; + +interface AuthProviderProps { + children: ReactNode; +} + +export const AuthProvider = ({ children }: AuthProviderProps) => { + const [user, setUser] = useState(null); + const [loading, setLoading] = useState(true); + + const isAdminDomain = () => { + if (typeof window === "undefined") return false; + return ( + window.location.hostname === "admin.garudahacks.com" || + window.location.hostname === "localhost" // TODO: Add flag for dev to toggle this + ); + }; + + const isAllowedDomain = (email: string): boolean => { + if (!isAdminDomain()) return true; + return email.endsWith("@garudahacks.com"); + }; + + const signIn = async (email: string, password: string) => { + if (!isAllowedDomain(email)) { + throw new Error( + "Only @garudahacks.com email addresses are allowed for admin access" + ); + } + + try { + await signInWithEmailAndPassword(auth, email, password); + } catch (error: any) { + throw new Error(error.message); + } + }; + + const signOut = async () => { + try { + await firebaseSignOut(auth); + } catch (error: any) { + throw new Error(error.message); + } + }; + + const resetPassword = async (email: string) => { + if (!isAllowedDomain(email)) { + throw new Error( + "Only @garudahacks.com email addresses are allowed for admin access" + ); + } + + try { + await sendPasswordResetEmail(auth, email); + } catch (error: any) { + throw new Error(error.message); + } + }; + + const signInWithGoogle = async () => { + try { + const result = await signInWithPopup(auth, googleProvider); + + // Double-check the domain after sign-in + if (!isAllowedDomain(result.user.email || "")) { + await firebaseSignOut(auth); + throw new Error("Only @garudahacks.com email addresses are allowed"); + } + + // Optional: Check if the user's email is verified and from the right domain + const credential = GoogleAuthProvider.credentialFromResult(result); + if (credential) { + // You can access additional user info here if needed + console.log("User signed in successfully:", result.user.email); + } + } catch (error: any) { + // Handle specific error cases + if (error.code === "auth/popup-closed-by-user") { + throw new Error("Sign-in was cancelled"); + } else if (error.code === "auth/popup-blocked") { + throw new Error("Popup was blocked. Please allow popups and try again"); + } else { + throw new Error(error.message); + } + } + }; + + useEffect(() => { + const unsubscribe = onAuthStateChanged(auth, (user) => { + if (user && !isAllowedDomain(user.email || "")) { + // Sign out user if they don't have allowed domain + firebaseSignOut(auth); + setUser(null); + console.error("User signed out: invalid domain"); + } else { + setUser(user); + } + setLoading(false); + }); + + return () => unsubscribe(); + }, []); + + return ( + + {children} + + ); +}; diff --git a/firebase.json b/firebase.json new file mode 100644 index 0000000..96a6145 --- /dev/null +++ b/firebase.json @@ -0,0 +1,22 @@ +{ + "firestore": { + "database": "(default)", + "location": "asia-southeast2", + "rules": "firestore.rules", + "indexes": "firestore.indexes.json" + }, + "hosting": { + "public": "out", + "ignore": [ + "firebase.json", + "**/.*", + "**/node_modules/**" + ], + "rewrites": [ + { + "source": "**", + "destination": "/index.html" + } + ] + } +} diff --git a/firestore.indexes.json b/firestore.indexes.json new file mode 100644 index 0000000..2ddb5ce --- /dev/null +++ b/firestore.indexes.json @@ -0,0 +1,4 @@ +{ + "indexes": [], + "fieldOverrides": [] +} \ No newline at end of file diff --git a/firestore.rules b/firestore.rules new file mode 100644 index 0000000..3ae7c62 --- /dev/null +++ b/firestore.rules @@ -0,0 +1,9 @@ +rules_version = '2'; + +service cloud.firestore { + match /databases/{database}/documents { + match /users/{userId} { + allow read, write: if request.auth != null && request.auth.uid == userId; + } + } +} \ No newline at end of file diff --git a/lib/firebase.ts b/lib/firebase.ts new file mode 100644 index 0000000..32e2258 --- /dev/null +++ b/lib/firebase.ts @@ -0,0 +1,29 @@ +import { initializeApp } from 'firebase/app'; +import { getAuth, GoogleAuthProvider } from 'firebase/auth'; +import { getFirestore } from 'firebase/firestore'; + +const firebaseConfig = { + apiKey: process.env.NEXT_PUBLIC_FIREBASE_API_KEY, + authDomain: process.env.NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN, + projectId: process.env.NEXT_PUBLIC_FIREBASE_PROJECT_ID, + storageBucket: process.env.NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET, + messagingSenderId: process.env.NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID, + appId: process.env.NEXT_PUBLIC_FIREBASE_APP_ID, + measurementId: process.env.NEXT_PUBLIC_FIREBASE_MEASUREMENT_ID +}; + +// Initialize Firebase +const app = initializeApp(firebaseConfig); + +// Initialize Firebase Auth +export const auth = getAuth(app); + +// Initialize Firestore +export const db = getFirestore(app); + +export const googleProvider = new GoogleAuthProvider(); +googleProvider.setCustomParameters({ + hd: "garudahacks.com", +}); + +export default app; diff --git a/lib/firebaseUtils.ts b/lib/firebaseUtils.ts new file mode 100644 index 0000000..e69de29 diff --git a/public/404.html b/public/404.html new file mode 100644 index 0000000..829eda8 --- /dev/null +++ b/public/404.html @@ -0,0 +1,33 @@ + + + + + + Page Not Found + + + + +
+

404

+

Page Not Found

+

The specified file was not found on this website. Please check the URL for mistakes and try again.

+

Why am I seeing this?

+

This page was generated by the Firebase Command-Line Interface. To modify it, edit the 404.html file in your project's configured public directory.

+
+ + diff --git a/public/index.html b/public/index.html new file mode 100644 index 0000000..248ff1a --- /dev/null +++ b/public/index.html @@ -0,0 +1,89 @@ + + + + + + Welcome to Firebase Hosting + + + + + + + + + + + + + + + + + + + +
+

Welcome

+

Firebase Hosting Setup Complete

+

You're seeing this because you've successfully setup Firebase Hosting. Now it's time to go build something extraordinary!

+ Open Hosting Documentation +
+

Firebase SDK Loading…

+ + + + From 9b8c5ba8b6d63153227d67a5d7d6c3f411cd20b5 Mon Sep 17 00:00:00 2001 From: Kezia Date: Sat, 31 May 2025 01:58:06 -0400 Subject: [PATCH 03/89] make sidebar collapsible --- app/globals.css | 2 - app/layout.tsx | 5 +- components/Sidebar.tsx | 215 +++++++++++++++++++++++++++++------------ 3 files changed, 156 insertions(+), 66 deletions(-) diff --git a/app/globals.css b/app/globals.css index 403b11a..890ef09 100644 --- a/app/globals.css +++ b/app/globals.css @@ -9,7 +9,6 @@ -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; - /* Custom Color Palette */ --background: #001745; --background-secondary: #020617; --foreground: #ff0068; @@ -55,7 +54,6 @@ body { line-height: 1.6; } -/* Base component styles */ .btn-primary { background-color: var(--primary); color: var(--primary-foreground); diff --git a/app/layout.tsx b/app/layout.tsx index c89692d..6952d54 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -27,7 +27,10 @@ export default function RootLayout({
-
+
{children}
diff --git a/components/Sidebar.tsx b/components/Sidebar.tsx index a5b7e30..543406b 100644 --- a/components/Sidebar.tsx +++ b/components/Sidebar.tsx @@ -3,12 +3,20 @@ import { useAuth } from "@/contexts/AuthContext"; import { usePathname } from "next/navigation"; import Link from "next/link"; -import { useState } from "react"; +import { useState, useEffect } from "react"; export default function Sidebar() { const { user, signOut } = useAuth(); const pathname = usePathname(); const [showProfileMenu, setShowProfileMenu] = useState(false); + const [isCollapsed, setIsCollapsed] = useState(false); + + useEffect(() => { + document.documentElement.style.setProperty( + "--sidebar-width", + isCollapsed ? "4rem" : "16rem" + ); + }, [isCollapsed]); const handleSignOut = async () => { try { @@ -98,51 +106,98 @@ export default function Sidebar() { ]; return ( -
-
+
+
-
+
GH
-
-

- Garuda Hacks -

-

Admin Panel

-
+ {!isCollapsed && ( +
+

+ Garuda Hacks +

+

Admin Portal

+
+ )}
-