diff --git a/.github/workflows/auto-comment-pr-merge.yml b/.github/workflows/auto-comment-pr-merge.yml new file mode 100644 index 0000000..20b974a --- /dev/null +++ b/.github/workflows/auto-comment-pr-merge.yml @@ -0,0 +1,37 @@ +name: Auto Comment on PR Merge + +on: + pull_request_target: + types: [closed] + +permissions: + issues: write + pull-requests: write + +jobs: + comment: + if: github.event.pull_request.merged == true + runs-on: ubuntu-latest + + steps: + - name: Add Comment to Merged PR + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + COMMENT=$(cat < ## 📑Table of Contents - - [Getting Started](#getting-started) - [How Can I Contribute?](#how-can-i-contribute) - [Team Onboarding](#team-onboarding) @@ -24,17 +23,33 @@ Curious Connect is your go-to platform for revolutionizing virtual collaboration ## 🚀Getting Started ### Prerequisites -- Ensure you have an IDE installed on your local machine. - -1). Fork the repository on GitHub. -2). Clone your forked repository locally: - `git clone https://github.com/Curious-Ecosystem/Curious-Connect.git` -3). Change your directory to the project: `cd CURIOUS-CONNECT` -4). For the instructions in the [Project Setup](docs/Project_Setup.md) documentation to install the rquired dependencies and set up your development environment. - -By following these steps, you'll have a local copy of the Curious Connect project ready for development. For more detailed information, refer to our [Documentation](docs/Overview.md) - +- Ensure you have an IDE installed on your local machine. +- Fork the repository +- Clone your forked repository locally: +```bash + git clone https://github.com/Curious-Ecosystem/Curious-Connect.git + +``` +- Change your directory to the project: +```bash + cd CURIOUS-CONNECT + +``` + +- For the instructions in the [Project Setup](docs/Project_Setup.md) documentation to install the rquired dependencies and set up your development environment. + +- Install the necessary dependencies: +```bash + npm install +``` +- Start the NodeJS server: + +- Start the development server +```bash + npm start + ``` +- Open your browser and navigate to http://localhost:3000 to view the website ## 🤝How Can I Contribute? ### 🌟Team Onboarding @@ -43,6 +58,7 @@ By following these steps, you'll have a local copy of the Curious Connect projec ### 📜Contributing Guidelines - Before you start contributing, please take a moment to read our [Contributing guidelines](./CONTRIBUTING.md) to ensure a smooth and effective contribution process. +- Then Start the Journey of Open Source ## 📝Code of Conduct @@ -58,6 +74,38 @@ This project is licensed under the [BSD 4-Clause](./LICENSE.md) license. ## Our Contributors We appreciate your contributions and look forward to collaborating with you! +## Issue Resolved By + +### 1 [Aman Kumar Gupta :](https://github.com/AmanGupta2626) + +
    +
  1. Implemented Footer Section
  2. +
  3. Implemented FAQ Section
  4. +
  5. Implemented Signin/signup page
  6. +
  7. Implemented authentication using firebase
  8. +
  9. Added caption part which convert speech to text
  10. + +### 1. [Sheetal Tyagi :](https://github.com/Sheetal-04) + +
      +
    1. Implemented Meeting Cards.
    2. +
    3. Implemented Scrollbar based on theme
    4. +
    5. Implemented Smooth Scrolling
    6. +
    + +## Issue Resolved By +### 1. [Domeshwer Sahu :](https://github.com/domesh-is-Coding) +
      +
    1. User Avatar for Meeting Participants
    2. +
    3. UI for Multiple Participants
    4. +
    5. Implemented Random Avatar Color For Each Participants
    6. +
    7. Merged Joining Window with Meeting Room UI
    8. +
    9. Enhanced Functionalities of Joining Window
    10. +
    11. Added Feature for Selecting User Media (i.e. Camera & Microphone) Dynamically.
    12. +
    13. Enhanced Functionalities of Meeting Room Page.
    14. + +
    +

    Our Contributors❤️

    diff --git a/frontend/index.html b/frontend/index.html index ad2a4fc..74316d0 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -8,6 +8,8 @@
    + + diff --git a/frontend/package.json b/frontend/package.json index f450de5..b0bb4bf 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -11,19 +11,24 @@ "preview": "vite preview" }, "dependencies": { + "@chakra-ui/react": "^2.8.2", "@material-tailwind/react": "^2.1.9", "@remixicon/react": "^4.2.0", "express": "^4.19.2", + "firebase": "^10.12.0", "flowbite": "^2.3.0", "flowbite-react": "^0.9.0", + "framer-motion": "^11.2.6", "mongoose": "^8.3.5", "prettier": "^3.2.5", "react": "^18.2.0", "react-datepicker": "^6.9.0", "react-dom": "^18.2.0", + "react-hot-toast": "^2.4.1", "react-icons": "^5.2.1", "react-multi-carousel": "^2.8.5", "react-router-dom": "^6.23.1", + "react-simple-typewriter": "^5.0.1", "react-slick": "^0.30.2", "slick-carousel": "^1.8.1" }, diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index daaf0f2..ec92202 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -10,6 +10,7 @@ import TermsOfService from './Components/common/Footer/LegalSection/TermsOfServi import CreateMeeting from './Pages/Create-Meeting-Page'; import MeetingDetails from './Components/Pages/Meeting-Page/Meeting-Details'; import MeetingAvailability from './Components/Pages/Meeting-Page/Meeting-Availability'; +import ErrorPage from './Pages/Error-Page'; import Navbar from './Components/Pages/Landing-Page/Navbar'; const App = () => { @@ -38,6 +39,8 @@ const App = () => { path='/meetingDetails/meetingAvailaibility' element={} /> + {/* Error */} + } />
    diff --git a/frontend/src/Components/Pages/Error-Page/ErrorPage.jsx b/frontend/src/Components/Pages/Error-Page/ErrorPage.jsx new file mode 100644 index 0000000..58b5faf --- /dev/null +++ b/frontend/src/Components/Pages/Error-Page/ErrorPage.jsx @@ -0,0 +1,25 @@ +import React from 'react'; +import backgroundImage from '../../../assets/Landing-Page-Assets/ErrorImage.png'; + +const Error = () => { + return ( +
    +
    +

    Coming soon ...

    + +
    +
    + ); +}; + +export default Error; diff --git a/frontend/src/Components/Pages/Landing-Page/HeroSectionMobileView.jsx b/frontend/src/Components/Pages/Landing-Page/HeroSectionMobileView.jsx new file mode 100644 index 0000000..59aef32 --- /dev/null +++ b/frontend/src/Components/Pages/Landing-Page/HeroSectionMobileView.jsx @@ -0,0 +1,95 @@ +import React from 'react'; +import img from '../../../assets/Landing-Page-Assets/hero-mobile.png'; +import JoinWithCode from './JoinWithCode'; + +// Design Hero section here and call this hero section in "frontend/src/Pages/Landing-Page.jsx" + +const HeroSectionMobileView = () => { + return ( +
    +
    +
    +

    + CONNECT +

    +

    + COLLABORATE +

    +

    + CONFERENCE +

    + + {/* paragraph */} +
    +

    + Start your next video call with a single click. No download, + plug-in, or login is required. Just get straight to talking, + messaging, and sharing your screen. +

    +
    + + {/* image div */} +
    + + + + + + + + + home +
    +
    + + {/* buttons */} +
    + + +
    +
    +
    +
    + +
    +
    + ); +}; + +export default HeroSectionMobileView; diff --git a/frontend/src/Components/Pages/Landing-Page/JoinWithCode.jsx b/frontend/src/Components/Pages/Landing-Page/JoinWithCode.jsx index 9593fc8..c7a5c7b 100644 --- a/frontend/src/Components/Pages/Landing-Page/JoinWithCode.jsx +++ b/frontend/src/Components/Pages/Landing-Page/JoinWithCode.jsx @@ -1,25 +1,150 @@ -import React from 'react'; +import React, { useState, useEffect } from 'react'; +import { motion } from 'framer-motion'; import img2 from '../../../assets/Landing-Page-Assets/refresh.png'; + const JoinWithCode = () => { + const [meetingCode, setMeetingCode] = useState(''); + const [isMobile, setIsMobile] = useState(window.innerWidth <= 768); + + const handleInputChange = (e) => { + setMeetingCode(e.target.value); + }; + + const handleJoinRoom = () => { + // Perform actions when the "Join Room" button is clicked + console.log('Joining room with code:', meetingCode); + }; + + const updateMedia = () => { + setIsMobile(window.innerWidth <= 768); + }; + + useEffect(() => { + window.addEventListener('resize', updateMedia); + return () => window.removeEventListener('resize', updateMedia); + }); + + const containerStyle = { + display: 'flex', + flexDirection: 'row', + justifyContent: 'center', + alignItems: 'center', + marginBottom: '40px', + padding: '20px', + fontWeight: 'bold', + color: 'white', + background: '#1e2228', + borderRadius: '20px', + boxShadow: '0 10px 20px rgba(0, 0, 0, 0.3)', + maxWidth: '90%', + margin: '20px auto', + fontFamily: 'Arial, sans-serif', + fontSize: '18px', + flexWrap: isMobile ? 'wrap' : 'nowrap', + textAlign: isMobile ? 'center' : 'left', + }; + return ( -
    -
    -
    - Paste the meeting code -
    - -
    - Join Room -
    - -
    -
    + +//
    +//
    +//
    +// Paste the meeting code +//
    +// +//
    +// Join Room +//
    +// +//
    +//
    + + + + Paste the meeting code + + + + Join Room + + + ); }; diff --git a/frontend/src/Components/Pages/Landing-Page/Navbar.jsx b/frontend/src/Components/Pages/Landing-Page/Navbar.jsx index 838ad41..29bb085 100644 --- a/frontend/src/Components/Pages/Landing-Page/Navbar.jsx +++ b/frontend/src/Components/Pages/Landing-Page/Navbar.jsx @@ -36,7 +36,9 @@ const Navbar = () => { whileHover={{ scale: 1.1, rotate: [0, -10, 0, 10, 0], transition: { duration: 0.3 } }} /> + + Curious Connect
    @@ -55,7 +57,7 @@ const Navbar = () => { className={ nav ? 'fixed left-0 top-0 w-[40%] h-full border-r border-r-gray-900 ease-in-out duration-500 bg-gradient z-50' - : 'fixed left-0 top-0 left-[-100%] ease-in-out duration-500' + : 'fixed top-0 left-[-100%] ease-in-out duration-500' }> { {item.label} - ))} + + +
    + + { + setActive(0); + }}> + {' '} + Home + + + + { + setActive(1); + }}> + {' '} + About + + + + { + setActive(2); + }}> + {' '} + Contact + + + + { + setActive(3); + }}> + {' '} + FAQ + +
    ); diff --git a/frontend/src/Components/common/Footer/AboutSection/AboutPage.jsx b/frontend/src/Components/common/Footer/AboutSection/AboutPage.jsx index 73be27c..86773c4 100644 --- a/frontend/src/Components/common/Footer/AboutSection/AboutPage.jsx +++ b/frontend/src/Components/common/Footer/AboutSection/AboutPage.jsx @@ -1,103 +1,351 @@ import React from 'react'; +import { useState } from 'react'; +function AboutPage() { + const features = [ + { + question: 'High-Quality Video Conferencing', + answer: + 'Experience crystal-clear video calls that make you feel like youre in the same room.', + }, + { + question: 'Screen Sharing Capabilities', + answer: + 'Share your screen with ease to present documents, slideshows, or any other content in real-time.', + }, + { + question: 'Real-Time Chat and Messaging', + answer: + 'Communicate instantly with participants through integrated chat and messaging features.', + }, + { + question: 'Secure End-to-End Encryption', + answer: + ' Ensure your meetings are private and secure with robust end-to-end encryption.', + }, + { + question: 'Customizable Meeting Settings', + answer: + ' Tailor your meetings to fit your specific needs with customizable settings, from participant permissions to meeting layouts.', + }, + { + question: 'Cross-Platform Compatibility', + answer: + 'Access Curious Connect from any device, whether its a desktop, laptop, tablet, or smartphone.', + }, + { + question: 'Recording and Playback', + answer: + ' Record your sessions for future reference or sharing with team members who couldnt attend.', + }, + ]; + const [openIndex, setOpenIndex] = useState(null); + + const handleToggle = (index) => { + setOpenIndex(openIndex === index ? null : index); + }; + + const Accordion = ({ data }) => { + return ( +
    + {data.map((el, i) => ( + handleToggle(i)} + /> + ))} +
    + ); + }; + + const AccordionItem = ({ question, answer, isOpen, onClick }) => { + return ( +
    + +
    +

    {answer}

    +
    +
    + ); + }; + + return ( +
    +
    +
    +

    + About Curious Connect +

    +

    + Curious Connect is your go-to platform for revolutionizing virtual + collaboration. Designed by Curious Ecosystem, it's a + microservice-based video conferencing application aimed at enhancing + remote team interactions, webinars, and online classes. +

    +

    + With a focus on seamless user experiences, secure authentication, + real-time communication, and optimized media processing, Curious + Connect empowers teams to connect effortlessly and achieve their + goals. +

    +

    + Explore Curious Connect to redefine how you collaborate online and + unlock new levels of productivity and engagement. +

    +
    +

    + Key Features +

    + +
    +

    + Curious Connect is more than just a video conferencing tool—it's a + comprehensive solution for modern, efficient, and secure online + collaboration. Join us and see how we can transform your virtual + meetings into productive, engaging experiences. +

    +import React, { useState } from 'react'; import { motion } from 'framer-motion'; -import { FaVideo, FaChartBar, FaComments, FaShieldAlt, FaCog } from 'react-icons/fa'; -function AboutPage() { +import encrypt from '../../../../assets/About-Page-Assets/encrypt.svg'; +import chatMsg from '../../../../assets/About-Page-Assets/chatMsg.svg'; +import videoConf from '../../../../assets/About-Page-Assets/videoConf.png'; +import customMeet from '../../../../assets/About-Page-Assets/customMeet.svg'; +import screenShare from '../../../../assets/About-Page-Assets/screenShared.svg'; + +import Slider from 'react-slick'; +import 'slick-carousel/slick/slick.css'; +import 'slick-carousel/slick/slick-theme.css'; +import { Typewriter } from 'react-simple-typewriter'; + +const AboutPage = () => { + const [currentIndex, setCurrentIndex] = useState(0); + const settings = { + dots: false, + infinite: true, + speed: 600, + slidesToShow: 3, + slidesToScroll: 1, + autoplay: true, + autoplaySpeed: 2000, + pauseOnHover: true, // Disable pausing on hover + cssEase: 'linear', // Add linear CSS easing + beforeChange: (oldIndex, newIndex) => { + setCurrentIndex(newIndex); + }, + responsive: [ + { + breakpoint: 1024, + settings: { + slidesToShow: 2, + slidesToScroll: 1, + infinite: true, + dots: false, + }, + }, + { + breakpoint: 600, + settings: { + slidesToShow: 1, + slidesToScroll: 1, + initialSlide: 1, + }, + }, + ], + }; + + const curiousFeatures = [ + { + name: 'High-quality video conferencing', + src: videoConf, + }, + { + name: 'Screen sharing capabilities', + src: screenShare, + }, + { + name: 'Real-time chat and messaging', + src: chatMsg, + }, + { + name: 'Secure end-to-end encryption', + src: encrypt, + }, + { + name: 'Customizable meeting settings', + src: customMeet, + }, + ]; + + const centerIndex = + (currentIndex + Math.floor(settings.slidesToShow / 2)) % + curiousFeatures.length; + + const sliderStyle = { cursor: 'grab' }; + + // const aboutHeading= document.querySelector('.aboutHeading'); + return ( -
    -
    - - About Curious Connect - - - Curious Connect is your go-to platform for revolutionizing virtual collaboration. Designed by Curious Ecosystem, it's a microservice-based video conferencing application aimed at enhancing remote team interactions, webinars, and online classes. - - - With a focus on seamless user experiences, secure authentication, real-time communication, and optimized media processing, Curious Connect empowers teams to connect effortlessly and achieve their goals. - - - Explore Curious Connect to redefine how you collaborate online and unlock new levels of productivity and engagement. - - -

    + <> + +
    +
    +
    + + Our Mission + + +

    + Empowering teams to{' '} + + + +
    their goals{' '} + effortlessly. +

    +
    + + Our Identity + + +
    +

    + + Curious Connect{' '} + + is your go-to platform for revolutionizing virtual + collaboration.
    +

    +

    + Designed by{' '} + + Curious Ecosystem + + , it's a microservice-based video conferencing application + aimed at enhancing remote team interactions, webinars, and + online classes. +

    +

    + With a focus on seamless User Experiences, Secure + Authentication, Real-time Communication and Optimized Media + processing. Explore Curious Connect to redefine how you{' '} + collaborate{' '} + online and unlock new levels of productivity and engagement. +

    +
    +
    +
    + Key Features -

    -
      - - - High-quality video conferencing - - - - Screen sharing capabilities - - - - Real-time chat and messaging - - - - Secure end-to-end encryption - - - - Customizable meeting settings - -
    -
    + + + + {curiousFeatures.map((feature, index) => ( +
    + (e.currentTarget.style.cursor = 'grabbing') + } + onMouseUp={(e) => (e.currentTarget.style.cursor = 'grab')} + onMouseLeave={(e) => (e.currentTarget.style.cursor = 'grab')} + className={`flex justify-center items-center flex-col mx-auto bg-white p-4 rounded-lg ${ + index === centerIndex ? 'centerSlide' : '' + }`}> +
    + {feature.name} + {feature.name} +
    +
    + ))} +
    +
    +
    -
    + ); -} +}; export default AboutPage; diff --git a/frontend/src/Components/common/Footer/AboutSection/FAQ.jsx b/frontend/src/Components/common/Footer/AboutSection/FAQ.jsx index 5681eac..2e329a0 100644 --- a/frontend/src/Components/common/Footer/AboutSection/FAQ.jsx +++ b/frontend/src/Components/common/Footer/AboutSection/FAQ.jsx @@ -28,6 +28,37 @@ const FAQ = () => { answer: 'While Curious Connect is primarily designed for professional use, individuals can also use it for personal video calls or online gatherings.', }, + { + question: 'How does Curious Connect support students and organizations?', + answer: + 'Curious Connect provides a platform for live streaming sessions where students can receive personalized guidance and support. Organizations can use it to conduct training, workshops, and seminars, enabling effective knowledge transfer and skill development.', + }, + { + question: ' How can I join a live streaming session on Curious Connect?', + answer: + 'You can join a live streaming session by registering on the Curious Connect platform. Once registered, you can browse upcoming sessions, register for those that interest you, and join using the provided link.', + }, + { + question: 'Can I host my own live streaming sessions on Curious Connect?', + answer: + 'Yes, you can host your own sessions. To become a host, sign up on Curious Connect, complete your profile, and follow the instructions to create and schedule live streaming sessions.', + }, + { + question: ' Can sessions be recorded for later viewing?', + answer: + 'Yes, sessions can be recorded and saved for later viewing. Hosts have the option to enable recording for their sessions, allowing participants to access the content at their convenience.', + }, + { + question: 'How do I get support if I encounter issues?', + answer: + 'If you encounter any issues, you can reach out to our support team via the help section on the Curious Connect platform. We also have a comprehensive FAQ and knowledge base to assist with common questions and troubleshooting.', + }, + { + question: + ' How can I provide feedback or suggest improvements for Curious Connect?', + answer: + 'We welcome feedback and suggestions! You can provide feedback through our platform or by opening an issue on our GitHub repository.', + }, ]; const [openIndex, setOpenIndex] = useState(null); diff --git a/frontend/src/Components/common/Toast.jsx b/frontend/src/Components/common/Toast.jsx new file mode 100644 index 0000000..cd6416d --- /dev/null +++ b/frontend/src/Components/common/Toast.jsx @@ -0,0 +1,19 @@ +import { toast } from 'react-hot-toast'; + +export const showLoadingToast = (promise, defaultMessages) => { + return toast.promise( + promise, + { + loading: defaultMessages.loading, + success: (data) => {data.message || defaultMessages.success}, + error: (error) => {error.message || defaultMessages.error}, + }, + { + position: 'top-center', + style: { + background: 'white', + color: 'black', + }, + } + ); +}; \ No newline at end of file diff --git a/frontend/src/Firebase/firebase-config.js b/frontend/src/Firebase/firebase-config.js new file mode 100644 index 0000000..e14bca1 --- /dev/null +++ b/frontend/src/Firebase/firebase-config.js @@ -0,0 +1,27 @@ +// Import the functions you need from the SDKs you need +import { getAuth, GoogleAuthProvider } from 'firebase/auth'; +import { getFirestore } from 'firebase/firestore'; +// Import the functions you need from the SDKs you need +import { initializeApp } from "firebase/app"; +import { getAnalytics } from "firebase/analytics"; +// TODO: Add SDKs for Firebase products that you want to use +// https://firebase.google.com/docs/web/setup#available-libraries + +// Your web app's Firebase configuration +// For Firebase JS SDK v7.20.0 and later, measurementId is optional +const firebaseConfig = { + apiKey: "AIzaSyDM3zqvyOeyXMOCDMfXWtl1yH7g6wdadh0", + authDomain: "awesome-griffin-421204.firebaseapp.com", + projectId: "awesome-griffin-421204", + storageBucket: "awesome-griffin-421204.appspot.com", + messagingSenderId: "133501638693", + appId: "1:133501638693:web:a452989f29cbfab903c7cc", + measurementId: "G-V7GPV4FVR5" +}; + +// Initialize Firebase +const app = initializeApp(firebaseConfig); +const analytics = getAnalytics(app); +export const auth = getAuth(app); +export const db = getFirestore(app); +export const provider = new GoogleAuthProvider(); diff --git a/frontend/src/Pages/Error-Page.jsx b/frontend/src/Pages/Error-Page.jsx new file mode 100644 index 0000000..1703152 --- /dev/null +++ b/frontend/src/Pages/Error-Page.jsx @@ -0,0 +1,12 @@ +import React from 'react'; +import ErrorPageContainer from '../Components/Pages/Error-Page/ErrorPage'; + +const ErrorPage = () => { + return ( +
    + +
    + ); +}; + +export default ErrorPage; diff --git a/frontend/src/Pages/Landing-Page.jsx b/frontend/src/Pages/Landing-Page.jsx index 5d1d971..f8b5f08 100644 --- a/frontend/src/Pages/Landing-Page.jsx +++ b/frontend/src/Pages/Landing-Page.jsx @@ -1,19 +1,31 @@ -import React from 'react'; +import React, { useState, useEffect } from 'react'; import MeetingCards from '../Components/Pages/Landing-Page/MeetingCards'; import OurProducts from '../Components/Pages/Landing-Page/OurProducts'; import JoinRoom from './JoinRoom-Page'; import HeroSection from '../Components/Pages/Landing-Page/HeroSection'; +import HeroSectionMobileView from '../Components/Pages/Landing-Page/HeroSectionMobileView'; import FeatureCardSection from '../Components/Pages/Landing-Page/FeatureCardSection'; import JoinWithCode from '../Components/Pages/Landing-Page/JoinWithCode'; +import Login from './Login'; import Navbar from '../Components/Pages/Landing-Page/Navbar'; export const LandingPage = () => { + const [isMobile, setIsMobile] = useState(window.innerWidth < 767); + + useEffect(() => { + const mediaQuery = window.matchMedia('(max-width: 767px)'); + const handleResize = () => setIsMobile(mediaQuery.matches); + mediaQuery.addEventListener('change', handleResize); + return () => mediaQuery.removeEventListener('change', handleResize); + }, []); + return (
    - + {isMobile ? : } +
    ); diff --git a/frontend/src/Pages/Login.jsx b/frontend/src/Pages/Login.jsx new file mode 100644 index 0000000..701e1e0 --- /dev/null +++ b/frontend/src/Pages/Login.jsx @@ -0,0 +1,107 @@ +import { Box, Button, Heading, Input } from '@chakra-ui/react' +import React, { useState } from 'react' +import { signInWithPopup, createUserWithEmailAndPassword, signOut, signInWithEmailAndPassword } from 'firebase/auth'; +import { auth, db, provider } from "../Firebase/firebase-config"; +import { doc, setDoc } from 'firebase/firestore'; + + +const Login = () => { + + const [emailSignUp, setEmailSignUp] = useState('') + const [passwordSignUp, setPasswordSignUp] = useState('') + + const [emailSignIn, setEmailSignIn] = useState('') + const [passwordSignIn, setPasswordSignIn] = useState('') + const Signup = async () => { + try { + const email = emailSignUp; + const password = passwordSignUp; + + const userCredential = await createUserWithEmailAndPassword(auth, email, password) + const user = userCredential.user; + + const usersCollectionRef = doc(db, 'users', user.uid); + await setDoc(usersCollectionRef, { email, password }) + + setEmailSignUp(""); + setPasswordSignUp(""); + } catch (error) { + console.log('error: ', error); + } + } + + const SignIn = async () => { + try { + const email = emailSignIn; + const password = passwordSignIn; + + const userCredential = await signInWithEmailAndPassword(auth, email, password); + const user = userCredential.user; + + setEmailSignIn("") + setPasswordSignIn("") + } catch (error) { + console.log('error: ', error); + } + } + + const signInWithGoogle = async () => { + try { + const userCredential = await signInWithPopup(auth, provider) + const user = userCredential.user + const name = user.displayName; + const email = user.email; + const profilePic = user.photoURL; + + const usersCollectionRef = doc(db, 'users', user.uid); + await setDoc(usersCollectionRef, { email, googleAuth: true }); + + } catch (error) { + console.log('error: ', error); + } + } + + // * Logout + const logout = async () => { + try { + await signOut(auth); + alert("logout") + } catch (error) { + console.log('error: ', error); + } + } + + return ( + + + Sign Up + + + setEmailSignUp(e.target.value)} /> + + + setPasswordSignUp(e.target.value)} /> + + + + + + + Sign In + + + setEmailSignIn(e.target.value)} /> + + + setPasswordSignIn(e.target.value)} /> + + + + + + + + ) +} + +export default Login \ No newline at end of file diff --git a/frontend/src/assets/About-Page-Assets/chatMsg.svg b/frontend/src/assets/About-Page-Assets/chatMsg.svg new file mode 100644 index 0000000..4c82bda --- /dev/null +++ b/frontend/src/assets/About-Page-Assets/chatMsg.svg @@ -0,0 +1,126 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/frontend/src/assets/About-Page-Assets/customMeet.svg b/frontend/src/assets/About-Page-Assets/customMeet.svg new file mode 100644 index 0000000..032d58e --- /dev/null +++ b/frontend/src/assets/About-Page-Assets/customMeet.svg @@ -0,0 +1,12 @@ + + + + ic_fluent_launcher_settings_24_regular + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/frontend/src/assets/About-Page-Assets/customMeet2.svg b/frontend/src/assets/About-Page-Assets/customMeet2.svg new file mode 100644 index 0000000..1ea38c5 --- /dev/null +++ b/frontend/src/assets/About-Page-Assets/customMeet2.svg @@ -0,0 +1,2 @@ + +70 Basic icons by Xicons.co \ No newline at end of file diff --git a/frontend/src/assets/About-Page-Assets/encrypt.svg b/frontend/src/assets/About-Page-Assets/encrypt.svg new file mode 100644 index 0000000..218a514 --- /dev/null +++ b/frontend/src/assets/About-Page-Assets/encrypt.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/frontend/src/assets/About-Page-Assets/screenShared.svg b/frontend/src/assets/About-Page-Assets/screenShared.svg new file mode 100644 index 0000000..ea97b0f --- /dev/null +++ b/frontend/src/assets/About-Page-Assets/screenShared.svg @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/frontend/src/assets/About-Page-Assets/videoConf.png b/frontend/src/assets/About-Page-Assets/videoConf.png new file mode 100644 index 0000000..a89265c Binary files /dev/null and b/frontend/src/assets/About-Page-Assets/videoConf.png differ diff --git a/frontend/src/assets/Landing-Page-Assets/ErrorImage.png b/frontend/src/assets/Landing-Page-Assets/ErrorImage.png new file mode 100644 index 0000000..6ff5a44 Binary files /dev/null and b/frontend/src/assets/Landing-Page-Assets/ErrorImage.png differ diff --git a/frontend/src/assets/Landing-Page-Assets/hero-mobile.png b/frontend/src/assets/Landing-Page-Assets/hero-mobile.png new file mode 100644 index 0000000..7c92126 Binary files /dev/null and b/frontend/src/assets/Landing-Page-Assets/hero-mobile.png differ diff --git a/frontend/src/custom-hooks/UseAuth.js b/frontend/src/custom-hooks/UseAuth.js new file mode 100644 index 0000000..4db38dc --- /dev/null +++ b/frontend/src/custom-hooks/UseAuth.js @@ -0,0 +1,25 @@ +import React, { useEffect, useState } from 'react'; +import { onAuthStateChanged } from 'firebase/auth'; +import { auth } from '../Firebase/firebase-config'; + +const UseAuth = () => { + + const [currentUser, setCurrentUser] = useState(null); + + useEffect(() => { + const unsubscribe = onAuthStateChanged(auth, (user) => { + if (user) { + setCurrentUser(user) + } else { + setCurrentUser(null); + } + }) + + // * cleanup; + return unsubscribe; + }, []) + + return currentUser; +} + +export default UseAuth \ No newline at end of file diff --git a/frontend/tailwind.config.js b/frontend/tailwind.config.js index 8f91148..64a1d0d 100644 --- a/frontend/tailwind.config.js +++ b/frontend/tailwind.config.js @@ -1,13 +1,10 @@ /** @type {import('tailwindcss').Config} */ export default { - content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}", - 'node_modules/flowbite-react/lib/esm/**/*.js' - ], + content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}", 'node_modules/flowbite-react/lib/esm/**/*.js'], theme: { extend: { - screens: { sm: "640px", md: "768px", @@ -39,9 +36,10 @@ export default { }, colors: { borderclr: "#408ACD", - } + }, }, }, + plugins: [ require('flowbite/plugin') ] diff --git a/package.json b/package.json index 784d5c4..34b3cf8 100644 --- a/package.json +++ b/package.json @@ -33,6 +33,9 @@ "react-icons": "^5.2.1", "react-router-dom": "^6.23.1", "yarn": "^1.22.22" + + "react-slick": "^0.30.2", + "slick-carousel": "^1.8.1" }, "devDependencies": { "prettier": "^3.2.5" diff --git a/services/Auth/src/controllers/auth.controller.js b/services/Auth/src/controllers/auth.controller.js index d87a358..f2330d6 100644 --- a/services/Auth/src/controllers/auth.controller.js +++ b/services/Auth/src/controllers/auth.controller.js @@ -1,16 +1,15 @@ const bcrypt = require("bcryptjs"); const User = require("../models/user.model"); -const { errorHadnler } = require("../utils/error"); +const { errorHandler } = require("../utils/error"); const cookie = require('cookie'); // Import the 'cookie' library -async function signup(req, res) { +async function signup(req, res, next) { try { const { name, email, password } = req.body; //Check if input is as expected or not if (!name || !password || !email) { - res.json (errorHadnler(401,'All fields must be filled')) - return + return next(errorHandler(401,'All fields must be filled')) } let user = await User.findOne({ email }); @@ -20,8 +19,7 @@ async function signup(req, res) { // if user already exists; if (user) { - res.json(errorHadnler(400, "user already exists")); - return + return next(errorHandler(400, "user already exists")); } user = new User({ @@ -58,7 +56,7 @@ async function signup(req, res) { return res.status(200).json({ ...userResponse, token }); } catch (error) { - res.status(500).json({ ...error }); + next(error); } } @@ -68,18 +66,15 @@ async function signin(req, res, next) { //Check if input is as expected or not if (!email || !password) { - res.json (errorHadnler(401,'All fields must be filled')) - return + return next(errorHandler(401,'All fields must be filled')) } let user = await User.findOne({ email }); - // console.log(user); // checking whether user exists or not; if (!user) { - res.json(errorHadnler(404, "User does not exists")); - return + return next(errorHandler(404, "User does not exists")); } const isMatch = await bcrypt.compare(password, user.password); @@ -87,8 +82,7 @@ async function signin(req, res, next) { // if the password not matched; if (!isMatch) { - res.json(errorHadnler(401, "Invalid password")); - return + return next(errorHandler(401, "Invalid password")); } // generating jwt token; @@ -115,8 +109,7 @@ async function signin(req, res, next) { return res.status(200).json({ token, userResposne }); } catch (error) { - console.log(error); - res.status(500).json({ ...error }); + next(error); } } diff --git a/services/Auth/src/main.js b/services/Auth/src/main.js index c398a1d..4e7e2ce 100644 --- a/services/Auth/src/main.js +++ b/services/Auth/src/main.js @@ -27,6 +27,17 @@ async function server() { app.listen(config.PORT, () => { console.log(`server is running at: http://localhost:${config.PORT}`); }); + app.use((err, req, res, next) => { + const statusCode = err.statusCode || 500; + const message = err.message || "Internal Server Error"; + const stack = config.NODE_ENV === "development" ? err.stack : undefined; + return res.status(statusCode).json({ + success: false, + statusCode, + message, + stack + }); + }); } module.exports = server; diff --git a/services/Auth/src/middleware/auth.middleware.js b/services/Auth/src/middleware/auth.middleware.js index b89162f..aca3865 100644 --- a/services/Auth/src/middleware/auth.middleware.js +++ b/services/Auth/src/middleware/auth.middleware.js @@ -15,7 +15,7 @@ const userauth = async (req, res, next) => { const user = await User.findOne({ _id: id }); - if (!user) return res.status(401, "please login again"); + if (!user) return next(errorHadnler(401, "please login again")); res.cookie("token", token, { httpOnly: true, maxAge: 24 * 60 * 60 * 1000 }); diff --git a/services/Auth/src/middleware/validate.schema.js b/services/Auth/src/middleware/validate.schema.js index 5d98c70..7e8f81c 100644 --- a/services/Auth/src/middleware/validate.schema.js +++ b/services/Auth/src/middleware/validate.schema.js @@ -1,14 +1,13 @@ +const { errorHandler } = require('../utils/error'); + const validate = (schema) => async (req, res, next) => { try { const parsedBody = await schema.parseAsync(req.body); req.body = parsedBody; next(); } catch (err) { - console.log(err); const yourerror = err.errors[0].message; - res.status(400).json({ - msg: yourerror, - }); + next(errorHandler(400, yourerror, err)); } } diff --git a/services/Auth/src/utils/error.js b/services/Auth/src/utils/error.js index 3d45bbb..db5c1e9 100644 --- a/services/Auth/src/utils/error.js +++ b/services/Auth/src/utils/error.js @@ -1,8 +1,11 @@ -const errorHadnler = (statusCode, message) => { +const errorHandler = (statusCode, message, err = null) => { const error = new Error(); error.statusCode = statusCode; error.message = message; + if (err) { + error.stack = err.stack; + } return error; }; -module.exports = { errorHadnler }; +module.exports = { errorHandler }; diff --git a/services/Meeting/controllers/meetingController.js b/services/Meeting/controllers/meetingController.js new file mode 100644 index 0000000..03743e0 --- /dev/null +++ b/services/Meeting/controllers/meetingController.js @@ -0,0 +1,102 @@ +const Meeting = require('../models/meetingModel'); + +const getAllMeetings = async (req, res) =>{ + try { + const meetings = await Meeting.find(); + res.status(200).json(meetings); + } catch (error) { + res.status(500).json({ message: 'Error retrieving meetings', error }); + } +} + +const getMeetingById = async (req, res)=> { + try { + const meeting = await Meeting.findById(req.params.id); + if (!meeting) { + return res.status(404).json({ message: 'Meeting not found' }); + } + res.status(200).json(meeting); + } catch (error) { + res.status(500).json({ message: 'Error retrieving meeting', error }); + } +} + +const createMeeting = async(req, res)=> { + try { + console.log(req.body); + const { title, description, host_id, start_time, end_time, participants } = req.body; + const newMeeting = new Meeting({ title, description, host_id, start_time, end_time, participants }); + const savedMeeting = await newMeeting.save(); + res.status(201).json(savedMeeting); + } catch (error) { + res.status(500).json({ message: 'Error creating meeting', error }); + } +} + +const updateMeeting = async(req, res) => { + try { + const { title, description, host_id, start_time, end_time, participants, status } = req.body; + const updatedMeeting = await Meeting.findByIdAndUpdate( + req.params.id, + { title, description, host_id, start_time, end_time, participants, status }, + { new: true } + ); + if (!updatedMeeting) { + return res.status(404).json({ message: 'Meeting not found' }); + } + res.status(200).json(updatedMeeting); + } catch (error) { + res.status(500).json({ message: 'Error updating meeting', error }); + } +} + +const deleteMeeting = async(req, res) => { + try { + const deletedMeeting = await Meeting.findByIdAndDelete(req.params.id); + if (!deletedMeeting) { + return res.status(404).json({ message: 'Meeting not found' }); + } + res.status(200).json({ message: 'Meeting deleted successfully' }); + } catch (error) { + res.status(500).json({ message: 'Error deleting meeting', error }); + } + } + +const joinMeeting = async (req, res) => { + try { + const meeting = await Meeting.findById(req.params.id); + if (!meeting) { + return res.status(404).json({ message: 'Meeting not found' }); + } + if (!meeting.participants.includes(req.body.user_id)) { + meeting.participants.push(req.body.user_id); + await meeting.save(); + } + res.status(200).json({ message: 'Successfully joined meeting' }); + } catch (error) { + res.status(500).json({ message: 'Error joining meeting', error }); + } + } + +const inviteParticipants = async (req, res) => { + try { + const { meeting_id, participants } = req.body; + const meeting = await Meeting.findById(meeting_id); + if (!meeting) { + return res.status(404).json({ message: 'Meeting not found' }); + } + // Assuming you have a function to send emails or notifications + await sendInvitations(participants, meeting); + res.status(200).json({ message: 'Invitations sent successfully' }); + } catch (error) { + res.status(500).json({ message: 'Error inviting participants', error }); + } + } + +// Mock function to send invitations +const sendInvitations = async (participants, meeting) => { + // Implement your email or notification sending logic here + console.log(`Sending invitations to: ${participants.join(', ')} for meeting: ${meeting.title}`); +} + +module.exports = {inviteParticipants, joinMeeting, deleteMeeting, updateMeeting, createMeeting, getMeetingById, getAllMeetings }; diff --git a/services/Meeting/index.js b/services/Meeting/index.js index 299fff0..e9c5689 100644 --- a/services/Meeting/index.js +++ b/services/Meeting/index.js @@ -2,6 +2,7 @@ const express = require('express'); const mongoose = require('mongoose'); const cors = require('cors'); const config = require('./config'); +const meetingRoutes = require('./routes/meetingRoute'); const app = express(); @@ -9,6 +10,7 @@ const app = express(); app.use(cors()); app.use(express.json()); +app.use('/meeting',meetingRoutes); const port = config.PORT; const dbUri = config.DB_URI; diff --git a/services/Meeting/models/meetingModel.js b/services/Meeting/models/meetingModel.js new file mode 100644 index 0000000..558c837 --- /dev/null +++ b/services/Meeting/models/meetingModel.js @@ -0,0 +1,38 @@ +const mongoose = require('mongoose'); + +const meetingSchema = new mongoose.Schema({ + title: { + type: String, + required: true + }, + description: { + type: String, + required: true + }, + host_id: { + type: mongoose.Schema.Types.ObjectId, + ref: 'User', + required: true + }, + start_time: { + type: Date, + required: true + }, + end_time: { + type: Date, + required: true + }, + participants: [{ + type: mongoose.Schema.Types.ObjectId, + ref: 'User' + }], + status: { + type: String, + enum: ['scheduled', 'ongoing', 'completed', 'cancelled'], + default: 'scheduled' + } +}); + +const Meeting = mongoose.model('Meeting', meetingSchema); + +module.exports = Meeting; diff --git a/services/Meeting/routes/meetingRoute.js b/services/Meeting/routes/meetingRoute.js new file mode 100644 index 0000000..71280d6 --- /dev/null +++ b/services/Meeting/routes/meetingRoute.js @@ -0,0 +1,26 @@ +const express = require('express'); +const router = express.Router(); +const {inviteParticipants, joinMeeting, deleteMeeting, updateMeeting, createMeeting, getMeetingById, getAllMeetings} = require('../controllers/meetingController'); // Adjust the path based on your folder structure + +// Route to get all meetings +router.get('/', getAllMeetings); + +// Route to get a meeting by ID +router.get('/:id', getMeetingById); + +// Route to create a new meeting +router.post('/', createMeeting); + +// Route to update an existing meeting +router.put('/:id', updateMeeting); + +// Route to delete a meeting +router.delete('/:id',deleteMeeting); + +// Route to join a meeting +router.post('/:id/join', joinMeeting); + +// Route to invite participants to a meeting +router.post('/:id/invite', inviteParticipants); + +module.exports = router;