diff --git a/.gitignore b/.gitignore index d82a81f..f2382a4 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,7 @@ # Client ignores # ------------------ client/node_modules/ +client/config/firebase.js client/dist/ client/.vite/ client/.env diff --git a/Bruno/Room APIs/Create Room.bru b/Bruno/Room APIs/Create Room.bru new file mode 100644 index 0000000..4958089 --- /dev/null +++ b/Bruno/Room APIs/Create Room.bru @@ -0,0 +1,36 @@ +meta { + name: Create Room + type: http + seq: 1 +} + +post { + url: https://guessync.onrender.com/api/room/create + body: json + auth: inherit +} + +headers { + Authorization: Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6IjNiZjA1MzkxMzk2OTEzYTc4ZWM4MGY0MjcwMzM4NjM2NDA2MTBhZGMiLCJ0eXAiOiJKV1QifQ.eyJuYW1lIjoiWW9nZXNoIiwiaXNzIjoiaHR0cHM6Ly9zZWN1cmV0b2tlbi5nb29nbGUuY29tL25vdC1ndWVzc3luYyIsImF1ZCI6Im5vdC1ndWVzc3luYyIsImF1dGhfdGltZSI6MTc1MDU5NDQ1MywidXNlcl9pZCI6InBwYmJqd21ueFBRTU90NXl0amh4aEx5eHA3TjIiLCJzdWIiOiJwcGJiandtbnhQUU1PdDV5dGpoeGhMeXhwN04yIiwiaWF0IjoxNzUwNTk0NDU1LCJleHAiOjE3NTA1OTgwNTUsImVtYWlsIjoidGVzdDFAdGVzdC5jb20iLCJlbWFpbF92ZXJpZmllZCI6ZmFsc2UsImZpcmViYXNlIjp7ImlkZW50aXRpZXMiOnsiZW1haWwiOlsidGVzdDFAdGVzdC5jb20iXX0sInNpZ25faW5fcHJvdmlkZXIiOiJwYXNzd29yZCJ9fQ.aw15qoR4P8ZcoClnvbP_fUitg32gRWCSJs6cWyHCo3qjZyCNysmk8ZDU_e03nFDWVMY6oClm_-sEtq_OZCA7fPxrFLNBI5akkRfaooY1wDeg5YciFi6MtyN_1aA4zWLyeouO0As3KAYE5JyzAxWcYbTG9okFII19QyvfGL84MkcMzZuys9S9XKU5SOfYwFDBj2X8NZD5K1U97KWAf1oznKDuMqu8mDCWu7aGus4VBhXj_9HtiZ8xOuD5PKjWzBzJUSwWg2_Svb-_hNXpf37h1Co2rm3yVTm3TMgeU8n7I22YRAF_oORbxRsnSz6GmhMcJm_kvbqyYNbUjlq15y4X1w + Content-Type: application/json +} + +body:json { + { + "uid": "user_123", + "name": "Yogesh", + "avatar": "https://i.imgur.com/avatar.png", + "useSpotify": true, + "playlistId": "37i9dQZF1DXcBWIGoYBM5M", + "code": "123456", + "rounds": 5, + "duration": 15, + "languages": ["Tamil", "Hindi"], + "rules": { + "showHints": true, + "enableChat": true + } + "language": "tamil" + } + +} diff --git a/Bruno/Room APIs/Join Room.bru b/Bruno/Room APIs/Join Room.bru new file mode 100644 index 0000000..f4bf2c2 --- /dev/null +++ b/Bruno/Room APIs/Join Room.bru @@ -0,0 +1,26 @@ +meta { + name: Join Room + type: http + seq: 2 +} + +post { + url: https://guessync.onrender.com/api/room/join + body: json + auth: inherit +} + +headers { + Authorization: Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6IjNiZjA1MzkxMzk2OTEzYTc4ZWM4MGY0MjcwMzM4NjM2NDA2MTBhZGMiLCJ0eXAiOiJKV1QifQ.eyJuYW1lIjoiWW9nZXNoIiwiaXNzIjoiaHR0cHM6Ly9zZWN1cmV0b2tlbi5nb29nbGUuY29tL25vdC1ndWVzc3luYyIsImF1ZCI6Im5vdC1ndWVzc3luYyIsImF1dGhfdGltZSI6MTc1MDU5NDQ1MywidXNlcl9pZCI6InBwYmJqd21ueFBRTU90NXl0amh4aEx5eHA3TjIiLCJzdWIiOiJwcGJiandtbnhQUU1PdDV5dGpoeGhMeXhwN04yIiwiaWF0IjoxNzUwNTk0NDU1LCJleHAiOjE3NTA1OTgwNTUsImVtYWlsIjoidGVzdDFAdGVzdC5jb20iLCJlbWFpbF92ZXJpZmllZCI6ZmFsc2UsImZpcmViYXNlIjp7ImlkZW50aXRpZXMiOnsiZW1haWwiOlsidGVzdDFAdGVzdC5jb20iXX0sInNpZ25faW5fcHJvdmlkZXIiOiJwYXNzd29yZCJ9fQ.aw15qoR4P8ZcoClnvbP_fUitg32gRWCSJs6cWyHCo3qjZyCNysmk8ZDU_e03nFDWVMY6oClm_-sEtq_OZCA7fPxrFLNBI5akkRfaooY1wDeg5YciFi6MtyN_1aA4zWLyeouO0As3KAYE5JyzAxWcYbTG9okFII19QyvfGL84MkcMzZuys9S9XKU5SOfYwFDBj2X8NZD5K1U97KWAf1oznKDuMqu8mDCWu7aGus4VBhXj_9HtiZ8xOuD5PKjWzBzJUSwWg2_Svb-_hNXpf37h1Co2rm3yVTm3TMgeU8n7I22YRAF_oORbxRsnSz6GmhMcJm_kvbqyYNbUjlq15y4X1w + Content-Type: application/json +} + +body:json { + { + "uid": "user_456", + "name": "Ryan", + "avatar": "https://i.imgur.com/avatar2.png", + "code": "123456" + } + +} diff --git a/Bruno/Room APIs/folder.bru b/Bruno/Room APIs/folder.bru new file mode 100644 index 0000000..aa7f251 --- /dev/null +++ b/Bruno/Room APIs/folder.bru @@ -0,0 +1,3 @@ +meta { + name: Room APIs +} diff --git a/Bruno/Song GET/Song-b-index.bru b/Bruno/Song GET/Song-b-index.bru new file mode 100644 index 0000000..9d7d452 --- /dev/null +++ b/Bruno/Song GET/Song-b-index.bru @@ -0,0 +1,16 @@ +meta { + name: Song-b-index + type: http + seq: 1 +} + +get { + url: https://guessync.onrender.com/api/song/by-index?roomCode=123486&index=0 + body: none + auth: inherit +} + +params:query { + roomCode: 123486 + index: 0 +} diff --git a/Bruno/Song GET/folder.bru b/Bruno/Song GET/folder.bru new file mode 100644 index 0000000..33bd8ca --- /dev/null +++ b/Bruno/Song GET/folder.bru @@ -0,0 +1,3 @@ +meta { + name: Song GET +} diff --git a/Bruno/User GET/User Authentication UID.bru b/Bruno/User GET/User Authentication UID.bru new file mode 100644 index 0000000..b9674fa --- /dev/null +++ b/Bruno/User GET/User Authentication UID.bru @@ -0,0 +1,15 @@ +meta { + name: User Authentication UID + type: http + seq: 1 +} + +get { + url: https://guessync.onrender.com/api/auth/me + body: none + auth: inherit +} + +headers { + Authorization: Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6IjNiZjA1MzkxMzk2OTEzYTc4ZWM4MGY0MjcwMzM4NjM2NDA2MTBhZGMiLCJ0eXAiOiJKV1QifQ.eyJuYW1lIjoiWW9nZXNoIiwiaXNzIjoiaHR0cHM6Ly9zZWN1cmV0b2tlbi5nb29nbGUuY29tL25vdC1ndWVzc3luYyIsImF1ZCI6Im5vdC1ndWVzc3luYyIsImF1dGhfdGltZSI6MTc1MDU5NDQ1MywidXNlcl9pZCI6InBwYmJqd21ueFBRTU90NXl0amh4aEx5eHA3TjIiLCJzdWIiOiJwcGJiandtbnhQUU1PdDV5dGpoeGhMeXhwN04yIiwiaWF0IjoxNzUwNTk0NDU1LCJleHAiOjE3NTA1OTgwNTUsImVtYWlsIjoidGVzdDFAdGVzdC5jb20iLCJlbWFpbF92ZXJpZmllZCI6ZmFsc2UsImZpcmViYXNlIjp7ImlkZW50aXRpZXMiOnsiZW1haWwiOlsidGVzdDFAdGVzdC5jb20iXX0sInNpZ25faW5fcHJvdmlkZXIiOiJwYXNzd29yZCJ9fQ.aw15qoR4P8ZcoClnvbP_fUitg32gRWCSJs6cWyHCo3qjZyCNysmk8ZDU_e03nFDWVMY6oClm_-sEtq_OZCA7fPxrFLNBI5akkRfaooY1wDeg5YciFi6MtyN_1aA4zWLyeouO0As3KAYE5JyzAxWcYbTG9okFII19QyvfGL84MkcMzZuys9S9XKU5SOfYwFDBj2X8NZD5K1U97KWAf1oznKDuMqu8mDCWu7aGus4VBhXj_9HtiZ8xOuD5PKjWzBzJUSwWg2_Svb-_hNXpf37h1Co2rm3yVTm3TMgeU8n7I22YRAF_oORbxRsnSz6GmhMcJm_kvbqyYNbUjlq15y4X1w +} diff --git a/Bruno/User GET/folder.bru b/Bruno/User GET/folder.bru new file mode 100644 index 0000000..96f36d5 --- /dev/null +++ b/Bruno/User GET/folder.bru @@ -0,0 +1,3 @@ +meta { + name: User GET +} diff --git a/Bruno/bruno.json b/Bruno/bruno.json new file mode 100644 index 0000000..c11314b --- /dev/null +++ b/Bruno/bruno.json @@ -0,0 +1,9 @@ +{ + "version": "1", + "name": "Bruno", + "type": "collection", + "ignore": [ + "node_modules", + ".git" + ] +} \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..c4ebc0c --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2025 Yogesh + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..a4500f4 --- /dev/null +++ b/README.md @@ -0,0 +1,212 @@ +

+ Guessync Demo +

+ +

+ + Guessync + +

+ +

+ A full-stack real-time multiplayer music guessing game
+ Compete with friends, guess songs, and use AI-powered hints! +

+ +--- + +## Features + +- **Spotify playlist integration** for song sourcing +- **YouTube audio streaming** for playback +- **AI-powered hints** using OpenAI +- **Real-time guessing & chat** via Socket.IO +- **Typo-tolerant matching** with Fuse.js +- **Secure authentication** (Firebase) +- **Modern frontend** (React + Vite + Tailwind) +- **Leaderboard scoring** in real time +- **MongoDB Atlas** for scalable data storage + +--- + +## Tech Stack + +### Frontend +- React + Vite +- Tailwind CSS +- Firebase Authentication +- Socket.IO Client + +### Backend +- Node.js + Express.js +- MongoDB (Mongoose) +- Firebase Admin SDK (auth verification) +- Socket.IO Server +- Spotify API + YouTube API +- Gemini API (AI hint generation) +- Bruno (API testing) + +--- + +## Deployment + +- **Frontend:** [Guessync on Netlify](https://guessync.netlify.app/) +- **Backend:** [Guessync on Render](https://guessync.onrender.com/) +- **Database:** MongoDB Atlas + +--- + +## Local Development (Step-by-step) + +Follow these steps to run both backend (Express) and frontend (Vite + React) locally. + +- Backend runs on: http://localhost:5000 +- Frontend runs on: http://localhost:5173 + +### 1) Prerequisites +- Node.js 18+ and npm (or yarn/pnpm) +- A MongoDB database (Atlas or local) +- Firebase project (for Auth) +- Optional, but recommended: + - Spotify API credentials (song sourcing) + - YouTube API key (audio) + - Gemini/OpenAI key (AI hints) + +### 2) Clone and enter the project +```bash +git clone +cd Guessync +``` + +### 3) Configure environment files + +Use the provided examples to create your .env files. + +- macOS/Linux: +```bash +cp server/.env.example server/.env +cp client/.env.example client/.env +``` + +- Windows PowerShell: +```powershell +Copy-Item server/.env.example server/.env +Copy-Item client/.env.example client/.env +``` + +Now fill the values: + +Server (.env): +- MONGO_URI: Your MongoDB connection string (Atlas or local, e.g. mongodb://127.0.0.1:27017/guessync) +- PORT: 5000 (default) +- SPOTIFY_CLIENT_ID / SPOTIFY_CLIENT_SECRET: From Spotify Developer Dashboard +- YOUTUBE_API_KEY: From Google Cloud Console +- GEMINI_API_KEY: From Google AI Studio (optional if you won’t use AI hints) +- Firebase service account: + - In Firebase console: Project settings → Service accounts → Generate new private key + - Paste fields into .env. Important: keep FIREBASE_PRIVATE_KEY wrapped in quotes and with escaped newlines: + FIREBASE_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----\n...your key...\n-----END PRIVATE KEY-----\n" + +Client (.env): +- VITE_FIREBASE_*: From Firebase project settings → General → Your apps (Web app) + +Tip: Never commit .env. It’s ignored by .gitignore already. + +### 4) Install dependencies + +From project root, install in both apps: + +```bash +cd server && npm install +cd ../client && npm install +``` + +### 5) Run backend and frontend + +Open two terminals: + +- Terminal A (Backend): +```bash +cd server +npm run dev +# Expect: "Server running on port 5000" +``` + +- Terminal B (Frontend): +```bash +cd client +npm run dev +# Open the shown local URL, usually http://localhost:5173 +``` + +### 6) Log in and try the app +- Use Email/Password or Google via Firebase +- Pick an avatar +- Create a room or join with a code +- Open a second browser window to simulate a friend joining + +--- + +## Common Issues & Troubleshooting + +- CORS error in browser console: + - The backend only allows these origins by default: + - http://localhost:5173 + - https://guessync.netlify.app + - If your dev URL differs (e.g. 127.0.0.1:5173), add it in server/server.js (allowedOrigins array). + +- Rate limiting (HTTP 429): + - /api/room/create and /api/auth/login are limited to 5 requests/min (see server/server.js with express-rate-limit). + - Wait a minute or increase limits for local dev only. + +- Firebase “invalid credential” or “private key” errors: + - Ensure FIREBASE_PRIVATE_KEY uses escaped newlines and is wrapped in quotes: + "-----BEGIN PRIVATE KEY-----\n...lines...\n-----END PRIVATE KEY-----\n" + - Make sure Client VITE_FIREBASE_* matches the same Firebase project. + +- MongoDB connection errors: + - For Atlas, whitelist your IP or set “Allow access from anywhere”. + - Verify MONGO_URI is correct and the cluster is running. + +- Vite port already in use: + - Run a different port: + npm run dev -- --port 5174 + +- Socket connection issues: + - Backend must be running on http://localhost:5000. + - Ensure your frontend’s socket base URL matches backend. + +--- + +## Scripts + +Backend (server/package.json): +- npm run dev → Nodemon on server.js + +Frontend (client/package.json): +- npm run dev → Vite dev server +- npm run build → Production build + +--- + +## Project Structure (high-level) + +- server/ (Express, Socket.IO, MongoDB, Firebase Admin, routes, cron) + - server.js (entry) + - routes/, sockets/, config/, cronJobs/ + - .env.example +- client/ (React + Vite, Tailwind, Firebase Auth, Socket.IO client) + - src/pages, src/components, src/assets + - vite.config.js, tailwind config + - .env.example + +--- + +## Contributing + +1) Fork the repo +2) Create a feature branch +3) Commit with clear messages +4) Open a PR with a brief description and screenshots if relevant + +Thanks for contributing! diff --git a/client/.env.example b/client/.env.example new file mode 100644 index 0000000..f7d53b0 --- /dev/null +++ b/client/.env.example @@ -0,0 +1,8 @@ +# Firebase (for Vite + Frontend) +VITE_FIREBASE_API_KEY="your_firebase_api_key" +VITE_FIREBASE_AUTH_DOMAIN="your_project.firebaseapp.com" +VITE_FIREBASE_PROJECT_ID="your_firebase_project_id" +VITE_FIREBASE_STORAGE_BUCKET="your_project.firebasestorage.app" +VITE_FIREBASE_MESSAGING_SENDER_ID="your_firebase_messaging_sender_id" +VITE_FIREBASE_APP_ID="your_firebase_app_id" + diff --git a/client/index.html b/client/index.html index 1f6f8b0..187b8d8 100644 --- a/client/index.html +++ b/client/index.html @@ -2,7 +2,7 @@ - + Guessync diff --git a/client/package-lock.json b/client/package-lock.json index 6ce1db8..6e2fa68 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -17,6 +17,7 @@ "colorthief": "^2.6.0", "firebase": "^11.6.0", "framer-motion": "^12.9.2", + "lucide-react": "^0.544.0", "react": "^19.0.0", "react-dom": "^19.0.0", "react-icons": "^5.5.0", @@ -41,7 +42,7 @@ "jsdom": "^26.1.0", "postcss": "^8.5.3", "tailwindcss": "^4.1.3", - "vite": "^6.2.0", + "vite": "^6.3.5", "vitest": "^3.2.4" } }, @@ -4464,7 +4465,6 @@ "version": "6.4.6", "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.6.tgz", "integrity": "sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==", - "dev": true, "license": "MIT", "peerDependencies": { "picomatch": "^3 || ^4" @@ -5364,6 +5364,15 @@ "yallist": "^3.0.2" } }, + "node_modules/lucide-react": { + "version": "0.544.0", + "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.544.0.tgz", + "integrity": "sha512-t5tS44bqd825zAW45UQxpG2CvcC4urOwn2TrwSH8u+MjeE+1NnWl6QqeQ/6NdjMqdOygyiT9p3Ev0p1NJykxjw==", + "license": "ISC", + "peerDependencies": { + "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, "node_modules/lz-string": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz", @@ -5654,7 +5663,6 @@ "version": "4.0.2", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", - "dev": true, "license": "MIT", "engines": { "node": ">=12" @@ -6422,7 +6430,6 @@ "version": "0.2.14", "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.14.tgz", "integrity": "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==", - "dev": true, "license": "MIT", "dependencies": { "fdir": "^6.4.4", @@ -6610,14 +6617,17 @@ } }, "node_modules/vite": { - "version": "6.2.5", - "resolved": "https://registry.npmjs.org/vite/-/vite-6.2.5.tgz", - "integrity": "sha512-j023J/hCAa4pRIUH6J9HemwYfjB5llR2Ps0CWeikOtdR8+pAURAk0DoJC5/mm9kd+UgdnIy7d6HE4EAvlYhPhA==", + "version": "6.3.5", + "resolved": "https://registry.npmjs.org/vite/-/vite-6.3.5.tgz", + "integrity": "sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ==", "license": "MIT", "dependencies": { "esbuild": "^0.25.0", + "fdir": "^6.4.4", + "picomatch": "^4.0.2", "postcss": "^8.5.3", - "rollup": "^4.30.1" + "rollup": "^4.34.9", + "tinyglobby": "^0.2.13" }, "bin": { "vite": "bin/vite.js" diff --git a/client/package.json b/client/package.json index 73ec407..33c9e6b 100644 --- a/client/package.json +++ b/client/package.json @@ -19,6 +19,7 @@ "colorthief": "^2.6.0", "firebase": "^11.6.0", "framer-motion": "^12.9.2", + "lucide-react": "^0.544.0", "react": "^19.0.0", "react-dom": "^19.0.0", "react-icons": "^5.5.0", @@ -43,7 +44,7 @@ "jsdom": "^26.1.0", "postcss": "^8.5.3", "tailwindcss": "^4.1.3", - "vite": "^6.2.0", + "vite": "^6.3.5", "vitest": "^3.2.4" } } diff --git a/client/public/_redirects b/client/public/_redirects new file mode 100644 index 0000000..bbb3e7a --- /dev/null +++ b/client/public/_redirects @@ -0,0 +1 @@ +/* /index.html 200 diff --git a/client/public/wordmark.png b/client/public/wordmark.png new file mode 100644 index 0000000..ea140bb Binary files /dev/null and b/client/public/wordmark.png differ diff --git a/client/src/App.jsx b/client/src/App.jsx index e69de29..3c5ef5d 100644 --- a/client/src/App.jsx +++ b/client/src/App.jsx @@ -0,0 +1,42 @@ +import { BrowserRouter as Router, Routes, Route } from "react-router-dom"; +import LandingPage from "./pages/LandingPage"; +import Login from "./pages/Login"; +import Signup from "./pages/Signup"; +import CreateRoom from './pages/CreateRoom'; +import JoinRoom from "./pages/JoinRoom"; +import WaitingRoom from "./pages/WaitingRoom"; +import GameRoom from "./pages/GameRoom"; +import Profile from "./pages/Profile"; +import HowToPlay from "./pages/HowToPlay"; +import HomePage from './pages/HomePage'; +import AboutDev from './pages/AboutDev'; + +function App() { + return ( + + + + ); +} + +// 👇 ADD this +function InnerApp() { + + return ( + + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + + ); +} + +export default App; diff --git a/client/src/assets/guessync.gif b/client/src/assets/guessync.gif new file mode 100644 index 0000000..a454f8e Binary files /dev/null and b/client/src/assets/guessync.gif differ diff --git a/client/src/components/LandingPage/AvatarSelector.jsx b/client/src/components/LandingPage/AvatarSelector.jsx index ad71f61..1996518 100644 --- a/client/src/components/LandingPage/AvatarSelector.jsx +++ b/client/src/components/LandingPage/AvatarSelector.jsx @@ -34,60 +34,76 @@ export default function AvatarSelector({ selectedAvatar = null, disabled = false const isLocked = selected || disabled; return ( -
-

Choose An Avatar

+
+ {/* Title */} +

+ Choose An Avatar +

-
+ {/* Avatar + Arrows */} +
+ {/* Left button */} + {/* Avatar preview */}
avatar
+ {/* Right button */}
+ {/* Select button */}
); -} +} \ No newline at end of file diff --git a/client/src/components/common/AvatarGrid.jsx b/client/src/components/common/AvatarGrid.jsx index 12afb67..b98b9f8 100644 --- a/client/src/components/common/AvatarGrid.jsx +++ b/client/src/components/common/AvatarGrid.jsx @@ -19,7 +19,7 @@ const AvatarGrid = ({ selected, onSelect }) => { onSelect(null); setIsSpinning(true); - let totalSteps = 25 + Math.floor(Math.random() * 8); + let totalSteps = 20 + Math.floor(Math.random() * 8); let current = 0; const spin = () => { @@ -40,7 +40,7 @@ const AvatarGrid = ({ selected, onSelect }) => { }; return ( -
+
{Array.from({ length: avatarCount }).map((_, i) => { const isSelected = selected === `/avatars/${i + 1}.png`; const isHighlighted = highlighted === i; @@ -48,10 +48,10 @@ const AvatarGrid = ({ selected, onSelect }) => {
handleSelect(i)} - className={`w-24 h-24 rounded-xl overflow-hidden flex items-center justify-center cursor-pointer transition-all duration-200 + className={`w-20 h-20 sm:w-24 sm:h-24 md:w-28 md:h-28 rounded-xl overflow-hidden flex items-center justify-center cursor-pointer transition-all duration-200 ${isSelected || isHighlighted ? "border-4 border-[#FFFB00] bg-[#FFFB00]/20 opacity-100 shadow-[0_0_15px_#FFFB00]" - : "opacity-30 border-2 border-gray-600"} + : "opacity-40 border-2 border-gray-600"} hover:opacity-100 hover:shadow-[0_0_10px_#FFFB00]`} > { ); })} + {/* Dice Button */}
- Random + Random
); }; -export default AvatarGrid; +export default AvatarGrid; \ No newline at end of file diff --git a/client/src/components/common/Navbar.jsx b/client/src/components/common/Navbar.jsx index 5701a3e..915a1f2 100644 --- a/client/src/components/common/Navbar.jsx +++ b/client/src/components/common/Navbar.jsx @@ -1,58 +1,110 @@ +import { useState } from "react"; import { useNavigate } from "react-router-dom"; -import avatar from "/avatars/1.png"; - +import { Menu, X } from "lucide-react"; // hamburger + close icons +import avatar from "/avatars/1.png"; export default function Navbar() { const navigate = useNavigate(); - + const [menuOpen, setMenuOpen] = useState(false); + + const navItems = [ + { label: "Home", path: "/landing" }, + { label: "New Room", path: "/create-room" }, + { label: "About Dev", path: "/about" }, + { label: "How to Play", path: "/how-to-play" }, + ]; return ( -