diff --git a/client/src/App.js b/client/src/App.js
index d5b5aaf..9e6a0ec 100644
--- a/client/src/App.js
+++ b/client/src/App.js
@@ -6,8 +6,13 @@ import { useSelector } from 'react-redux';
import { CssBaseline, ThemeProvider } from '@mui/material';
import { createTheme } from '@mui/material/styles';
import { themeSettings } from 'theme';
-import HomePage from 'scenes/homePage';
+
import LandingPage from 'scenes/landingPage';
+import ProfilePage from 'scenes/profilePage';
+import EventsPage from 'scenes/eventsPage';
+import HomePage from 'scenes/homePage';
+import EventDetailsPage from 'scenes/eventDetailsPage';
+import EventForm from 'scenes/eventForm';
const App = () => {
const mode = useSelector((state) => state.mode);
@@ -20,9 +25,31 @@ const App = () => {
- } />
- } />
- : } />
+ } />
+ : }
+ />
+ : }
+ />
+ : }
+ />
+ : }
+ />
+ : }
+ />
+ : }
+ />
diff --git a/client/src/assets/about.jpg b/client/src/assets/about.jpg
new file mode 100644
index 0000000..35ed493
Binary files /dev/null and b/client/src/assets/about.jpg differ
diff --git a/client/src/components/Follower.jsx b/client/src/components/Follower.jsx
new file mode 100644
index 0000000..c94875a
--- /dev/null
+++ b/client/src/components/Follower.jsx
@@ -0,0 +1,84 @@
+import { PersonAddOutlined, PersonRemoveOutlined } from "@mui/icons-material";
+import { Box, Typography, IconButton, useTheme } from "@mui/material";
+import { useDispatch, useSelector } from "react-redux";
+import { useNavigate } from "react-router-dom";
+import { setFollowers } from "state";
+import FlexBetween from "./FlexBetween";
+import UserImage from "./UserImage";
+
+const Follower = ({ followerId, name, subtitle, userPicturePath }) => {
+ const dispatch = useDispatch();
+ const navigate = useNavigate();
+ const { _id } = useSelector((state) => state.user);
+ const token = useSelector((state) => state.token);
+ const followers = useSelector((state) => state.user.followers);
+
+ const { palette } = useTheme();
+ const primaryLight = palette.primary.light;
+ const primaryDark = palette.primary.dark;
+ const main = palette.neutral.main;
+ const medium = palette.neutral.medium;
+
+ const isFollower = followers.find((follower) => follower._id === followerId);
+
+ const patchFollower = async () => {
+ const response = await fetch(
+ `http://localhost:3001/users/${_id}/${followerId}`,
+ {
+ method: "PATCH",
+ headers: {
+ Authorization: `Bearer ${token}`,
+ "Content-Type": "application/json",
+ },
+ }
+ );
+ const data = await response.json();
+ dispatch(setFollowers({ followers: data }));
+ };
+
+ return (
+
+
+
+ {
+ navigate(`/profile/${followerId}`);
+ navigate(0);
+ }}
+ >
+
+ {name}
+
+
+ {subtitle}
+
+
+
+ patchFollower()}
+ sx={{ backgroundColor: primaryLight, p: "0.6rem" }}
+ >
+ {isFollower ? (
+
+ ) : (
+
+ )}
+
+
+ );
+ };
+
+ export default Follower;
+
+
+
diff --git a/client/src/components/SearchBar.jsx b/client/src/components/SearchBar.jsx
new file mode 100644
index 0000000..28738b3
--- /dev/null
+++ b/client/src/components/SearchBar.jsx
@@ -0,0 +1,78 @@
+import React, { useState } from 'react';
+import { InputBase, IconButton, Paper, Popper, Typography, Box } from '@mui/material';
+import { Search as SearchIcon } from '@mui/icons-material';
+import { useNavigate } from 'react-router-dom';
+import { useSelector } from 'react-redux';
+
+const SearchBar = () => {
+ const [searchTerm, setSearchTerm] = useState('');
+ const [searchResults, setSearchResults] = useState(null);
+ const [anchorEl, setAnchorEl] = useState(null);
+ const navigate = useNavigate();
+ const token = useSelector((state) => state.token);
+
+ const handleSearch = async (event) => {
+ event.preventDefault();
+ setAnchorEl(event.currentTarget);
+
+ try {
+ const response = await fetch(`http://localhost:3001/search?query=${searchTerm}`, {
+ method: 'GET',
+ headers: { Authorization: `Bearer ${token}` },
+ });
+ const data = await response.json();
+ setSearchResults(data);
+ } catch (error) {
+ console.error('Error searching:', error);
+ }
+ };
+
+ const handleResultClick = (type, id) => {
+ setSearchResults(null);
+ setSearchTerm('');
+ if (type === 'user') navigate(`/profile/${id}`);
+ else if (type === 'event') navigate(`/event/${id}`);
+ };
+
+ return (
+ <>
+
+ setSearchTerm(e.target.value)}
+ />
+
+
+
+
+
+
+ {searchResults && (
+ <>
+ {/* Users */}
+ {searchResults.users.map((user) => (
+ handleResultClick('user', user._id)}>
+ {`${user.firstName} ${user.lastName}`}
+
+ ))}
+ {/* Events
+ {searchResults.events.map((event) => (
+ handleResultClick('event', event._id)}>
+ {event.title}
+
+ ))} */}
+ >
+ )}
+
+
+ >
+ );
+};
+
+export default SearchBar;
\ No newline at end of file
diff --git a/client/src/scenes/eventDetailsPage/index.jsx b/client/src/scenes/eventDetailsPage/index.jsx
new file mode 100644
index 0000000..ee9ae92
--- /dev/null
+++ b/client/src/scenes/eventDetailsPage/index.jsx
@@ -0,0 +1,141 @@
+import React, { useEffect, useState } from "react";
+import { Box, Typography, Button, useTheme } from "@mui/material";
+import { useParams, useNavigate } from "react-router-dom";
+import { useSelector } from "react-redux";
+import Navbar from "scenes/navbar";
+import WidgetWrapper from "components/WidgetWrapper";
+import FlexBetween from "components/FlexBetween";
+import UserImage from "components/UserImage";
+
+const EventDetailsPage = () => {
+ const [event, setEvent] = useState(null);
+ const [isAttending, setIsAttending] = useState(false);
+ const { eventId } = useParams();
+ const token = useSelector((state) => state.token);
+ const user = useSelector((state) => state.user);
+ const navigate = useNavigate();
+ const { palette } = useTheme();
+
+ const getEvent = async () => {
+ const response = await fetch(`http://localhost:3001/events/${eventId}`, {
+ method: "GET",
+ headers: { Authorization: `Bearer ${token}` },
+ });
+ const data = await response.json();
+ setEvent(data);
+ setIsAttending(data.attendees?.includes(user._id));
+ };
+
+ const handleAttendance = async () => {
+ const endpoint = isAttending ? 'unattend' : 'attend';
+ const response = await fetch(`http://localhost:3001/events/${eventId}/${endpoint}`, {
+ method: "POST",
+ headers: {
+ Authorization: `Bearer ${token}`,
+ "Content-Type": "application/json"
+ },
+ body: JSON.stringify({ userId: user._id })
+ });
+ if (response.ok) {
+ setIsAttending(!isAttending);
+ getEvent(); // Refresh event data
+ } else {
+ console.error("Failed to update attendance:", await response.text());
+ }
+ };
+
+ useEffect(() => {
+ getEvent();
+ }, []);
+
+ if (!event) return null;
+
+ const isCreator = user._id === event.creatorId?._id;
+
+ return (
+
+
+
+
+
+
+
+
+
+ {event.title}
+
+
+ Created by: {event.creatorId ? `${event.creatorId.firstName} ${event.creatorId.lastName}` : "Unknown"}
+
+
+
+
+
+ {event.description}
+
+
+ Date: {new Date(event.date).toLocaleString()}
+
+
+ Location: {event.location}
+
+
+ Attendees: {event.attendees?.length || 0}
+
+
+ {!isCreator && (
+
+ )}
+
+ {isCreator && (
+
+
+
+
+ )}
+
+
+
+ );
+};
+
+export default EventDetailsPage;
\ No newline at end of file
diff --git a/client/src/scenes/eventForm/index.jsx b/client/src/scenes/eventForm/index.jsx
new file mode 100644
index 0000000..4529213
--- /dev/null
+++ b/client/src/scenes/eventForm/index.jsx
@@ -0,0 +1,170 @@
+import React, { useState, useEffect } from "react";
+import { Box, Button, TextField, useMediaQuery, Typography, useTheme } from "@mui/material";
+import { Formik } from "formik";
+import * as yup from "yup";
+import { useNavigate, useParams } from "react-router-dom";
+import { useSelector } from "react-redux";
+import Navbar from "scenes/navbar";
+
+const eventSchema = yup.object().shape({
+ title: yup.string().required("required"),
+ description: yup.string().required("required"),
+ location: yup.string().required("required"),
+ date: yup.date().required("required"),
+});
+
+const initialValuesEvent = {
+ title: "",
+ description: "",
+ location: "",
+ date: "",
+};
+
+const EventForm = () => {
+ const { palette } = useTheme();
+ const navigate = useNavigate();
+ const isNonMobile = useMediaQuery("(min-width:600px)");
+ const token = useSelector((state) => state.token);
+ const { eventId } = useParams();
+ const [event, setEvent] = useState(null);
+
+ const getEvent = async () => {
+ const response = await fetch(`http://localhost:3001/events/${eventId}`, {
+ method: "GET",
+ headers: { Authorization: `Bearer ${token}` },
+ });
+ const data = await response.json();
+ setEvent(data);
+ };
+
+ useEffect(() => {
+ if (eventId) {
+ getEvent();
+ }
+ }, [eventId]);
+
+ const handleFormSubmit = async (values, onSubmitProps) => {
+ const url = eventId
+ ? `http://localhost:3001/events/${eventId}`
+ : "http://localhost:3001/events";
+ const method = eventId ? "PUT" : "POST";
+
+ const response = await fetch(url, {
+ method: method,
+ headers: {
+ Authorization: `Bearer ${token}`,
+ "Content-Type": "application/json",
+ },
+ body: JSON.stringify(values),
+ });
+ const savedEvent = await response.json();
+ onSubmitProps.resetForm();
+
+ if (savedEvent) {
+ navigate("/events");
+ }
+ };
+
+ if (eventId && !event) return null;
+
+ return (
+
+
+
+
+ {({
+ values,
+ errors,
+ touched,
+ handleBlur,
+ handleChange,
+ handleSubmit,
+ setFieldValue,
+ resetForm,
+ }) => (
+
+ )}
+
+
+
+ );
+};
+
+export default EventForm;
\ No newline at end of file
diff --git a/client/src/scenes/eventsPage/index.jsx b/client/src/scenes/eventsPage/index.jsx
new file mode 100644
index 0000000..6a149fd
--- /dev/null
+++ b/client/src/scenes/eventsPage/index.jsx
@@ -0,0 +1,84 @@
+import React, { useEffect, useState } from "react";
+import { Box, Button, useMediaQuery, useTheme } from "@mui/material";
+import { useSelector } from "react-redux";
+import { useNavigate } from "react-router-dom";
+import Navbar from "scenes/navbar";
+import EventWidget from "widgets/EventWidget";
+import UserWidget from "widgets/UserWidget";
+import FlexBetween from "components/FlexBetween";
+
+const EventsPage = () => {
+ const [events, setEvents] = useState([]);
+ const isNonMobileScreens = useMediaQuery("(min-width:1000px)");
+ const { _id, picturePath } = useSelector((state) => state.user);
+ const token = useSelector((state) => state.token);
+ const navigate = useNavigate();
+ const theme = useTheme();
+
+ const getEvents = async () => {
+ const response = await fetch("http://localhost:3001/events", {
+ method: "GET",
+ headers: { Authorization: `Bearer ${token}` },
+ });
+ const data = await response.json();
+ setEvents(data);
+ };
+
+ useEffect(() => {
+ getEvents();
+ }, []);
+
+ return (
+
+
+
+
+
+
+
+
+
+
+ {events.map((event) => (
+
+ ))}
+
+ {isNonMobileScreens && }
+
+
+ );
+};
+
+export default EventsPage;
\ No newline at end of file
diff --git a/client/src/scenes/homePage/index.jsx b/client/src/scenes/homePage/index.jsx
index 5bf2f04..212caff 100644
--- a/client/src/scenes/homePage/index.jsx
+++ b/client/src/scenes/homePage/index.jsx
@@ -5,6 +5,8 @@ import { useSelector } from "react-redux";
import UserWidget from "widgets/UserWidget";
import MyPostWidget from "widgets/MyPostWidget";
import PostsWidget from "widgets/PostsWidget";
+import AdvertWidget from "widgets/AdvertWidget";
+import FollowersListWidget from "widgets/FollowersListWidget";
const HomePage = () => {
const isNonMobileScreens = useMediaQuery("(min-width:1000px)");
@@ -31,7 +33,13 @@ const HomePage = () => {
- {isNonMobileScreens && }
+ {isNonMobileScreens && (
+
+
+
+
+
+ )}
)
diff --git a/client/src/scenes/landingPage/About.jsx b/client/src/scenes/landingPage/About.jsx
index 11ab551..1c4cab8 100644
--- a/client/src/scenes/landingPage/About.jsx
+++ b/client/src/scenes/landingPage/About.jsx
@@ -1,11 +1,153 @@
import React from "react";
+import { Box, Typography, useTheme, useMediaQuery, Grid, Avatar } from "@mui/material";
+import { Search, Group, Brush, Chat, Favorite, Explore, EmojiObjects, Stars } from "@mui/icons-material";
+import FlexBetween from "components/FlexBetween";
+import about from 'assets/about.jpg'; // Replace with your actual image path
+
+const AboutUs = () => {
+ const theme = useTheme();
+ const isNonMobileScreens = useMediaQuery("(min-width: 1000px)");
+
+ const values = [
+ { icon: , text: 'Curiosity' },
+ { icon: , text: 'Community' },
+ { icon: , text: 'Creativity' },
+ { icon: , text: 'Conversation' },
+ { icon: , text: 'Connection' },
+ { icon: , text: 'Discovery' },
+ { icon: , text: 'Inspiration' },
+ { icon: , text: 'Wonder' },
+ ];
+
+ const teamMembers = [
+ 'Member1', 'Member2', 'Member3', 'Member4', 'Member5'
+ ];
-const About = () => {
return (
-
-
About
-
- )
-}
+
+ {/* Hero Section */}
+
+
+
+ Welcome to Astrogram
+
+
+ Astrogram is a platform for astronomy enthusiasts to share and explore the universe. Our mission is to inspire curiosity, connect people, and foster a community of stargazers.
+
+
+
+
+ {/* Values Section */}
+
+
+ Our values
+
+
+ {values.map((value, i) => (
+
+
+ {value.icon}
+
+ {value.text}
+
+
+
+ ))}
+
+
+
+ {/* Team Section */}
+
+
+ Who we are
+
+
+ Meet the team
+
+
+ {teamMembers.map((member, i) => (
+
+
+
+
+
+
+ {member}
+
+
+
+ ))}
+
+
+
+ );
+};
-export default About;
\ No newline at end of file
+export default AboutUs;
\ No newline at end of file
diff --git a/client/src/scenes/navbar/index.jsx b/client/src/scenes/navbar/index.jsx
index 44d7b34..c7edc20 100644
--- a/client/src/scenes/navbar/index.jsx
+++ b/client/src/scenes/navbar/index.jsx
@@ -2,10 +2,12 @@ import React, { useState } from "react";
import { Box, IconButton, InputBase, Typography, Select, MenuItem, FormControl, useTheme, useMediaQuery } from "@mui/material";
import { Search, Message, DarkMode, LightMode, Help, Menu, Close } from "@mui/icons-material";
import { Notifications } from "@mui/icons-material";
+import { Event as EventIcon } from "@mui/icons-material";
import { useDispatch, useSelector } from "react-redux";
import { setMode, setLogout } from "state";
import { useNavigate } from "react-router-dom";
import FlexBetween from "components/FlexBetween";
+import SearchBar from "components/SearchBar";
const Navbar = () => {
const [isMobileMenuToggled, setIsMobileMenuToggled] = useState(false);
@@ -42,12 +44,13 @@ const Navbar = () => {
Astrogram
{isNonMobileScreens && (
-
-
-
-
-
-
+ //
+ //
+ //
+ //
+ //
+ //
+
)}
@@ -60,6 +63,9 @@ const Navbar = () => {
+ navigate("/events")}>
+
+