diff --git a/package-lock.json b/package-lock.json index 1da56e59..dc268f09 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,11 +1,11 @@ { - "name": "oauth-demo-frontend", + "name": "capstone-1-frontend", "version": "1.0.0", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "oauth-demo-frontend", + "name": "capstone-1-frontend", "version": "1.0.0", "license": "ISC", "dependencies": { diff --git a/src/App.jsx b/src/App.jsx index d793b9af..4572d174 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -4,14 +4,34 @@ import axios from "axios"; import "./AppStyles.css"; import NavBar from "./components/NavBar"; import { BrowserRouter as Router, Routes, Route } from "react-router-dom"; +import { API_URL } from "./shared"; + import Login from "./components/Login"; import Signup from "./components/Signup"; import Home from "./components/Home"; import NotFound from "./components/NotFound"; -import { API_URL } from "./shared"; +import FriendsPage from "./components/FriendsPage"; +import Friends from "./components/Friends"; +import Profile from "./components/Profile"; +import NewPoll from "./components/NewPoll"; +import PollList from "./components/PollList"; +import UsersPage from "./components/UsersPage"; +import UserCard from "./components/UserCard"; const App = () => { const [user, setUser] = useState(null); + const [polls, setPolls] = useState(null); + + const fetchPolls = async () => { + try{ + const response = await axios.get(`${API_URL}/api/polls`); + setPolls(response.data); + }catch{ + console.log("failed to get polls"); + setPolls([]); + } + }; + const checkAuth = async () => { try { @@ -25,14 +45,14 @@ const App = () => { } }; - // Check authentication status on app load + // Check authentication status and fetch polls on app load useEffect(() => { checkAuth(); + fetchPolls(); // Add this line to actually fetch polls }, []); const handleLogout = async () => { try { - // Logout from our backend await axios.post( `${API_URL}/auth/logout`, {}, @@ -53,7 +73,14 @@ const App = () => { } /> } /> + } /> } /> + } /> + } /> + } /> + } /> + } /> + } /> } /> @@ -61,6 +88,7 @@ const App = () => { ); }; + const Root = () => { return ( @@ -71,3 +99,5 @@ const Root = () => { const root = createRoot(document.getElementById("root")); root.render(); + +// \ No newline at end of file diff --git a/src/components/AuthStyles.css b/src/components/CSS/AuthStyles.css similarity index 100% rename from src/components/AuthStyles.css rename to src/components/CSS/AuthStyles.css diff --git a/src/components/CSS/Dropdown.css b/src/components/CSS/Dropdown.css new file mode 100644 index 00000000..d4ae23f7 --- /dev/null +++ b/src/components/CSS/Dropdown.css @@ -0,0 +1,37 @@ +.dropdown { + position: relative; + display: inline-block; +} + +.dropdown-toggle { + padding: 8px 14px; + font-size: 12px; + cursor: pointer; + border: 1px solid #cccccc; + border-radius: 4px; + width: 100%; + white-space: nowrap; +} + +.dropdown-menu { + position: absolute; + top: 100%; + left: 0; + z-index: 1000; + border: 1px solid #cccccc; + border-radius: 4px; + list-style: none; + padding: 0; + margin: 4px 0 0 0; + min-width: 100%; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); + background-color: #ffffff; +} + +.dropdown:hover .dropdown-menu { + display: block; +} + +.dropdown-item { + white-space: nowrap; +} \ No newline at end of file diff --git a/src/components/NavBarStyles.css b/src/components/CSS/NavBarStyles.css similarity index 100% rename from src/components/NavBarStyles.css rename to src/components/CSS/NavBarStyles.css diff --git a/src/components/CSS/PollCardStyles.css b/src/components/CSS/PollCardStyles.css new file mode 100644 index 00000000..4d91d8e0 --- /dev/null +++ b/src/components/CSS/PollCardStyles.css @@ -0,0 +1,96 @@ +.poll-card { + background: white; + border: 1px solid #e1e8ed; + border-radius: 12px; + padding: 16px; + margin-bottom: 12px; + transition: all 0.2s ease; + cursor: pointer; +} + +.poll-card:hover { + border-color: #1da1f2; + box-shadow: 0 2px 8px rgba(29, 161, 242, 0.1); +} + +.poll-card.poll-ended { + opacity: 0.7; + background: #f7f9fa; +} + +.poll-header { + margin-bottom: 12px; +} + +.poll-title { + font-size: 18px; + font-weight: 600; + color: #14171a; + margin: 0 0 8px 0; + line-height: 1.3; +} + +.poll-meta { + display: flex; + justify-content: space-between; + align-items: center; + flex-wrap: wrap; + gap: 8px; +} + +.poll-creator { + color: #657786; + font-size: 14px; + font-weight: 500; +} + +.poll-time { + background: #1da1f2; + color: white; + padding: 4px 8px; + border-radius: 12px; + font-size: 12px; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.5px; +} + +.poll-time.ended { + background: #657786; +} + +.poll-status { + margin-top: 8px; + display: flex; + justify-content: flex-end; +} + +.status-badge { + background: #f45d22; + color: white; + padding: 4px 12px; + border-radius: 16px; + font-size: 12px; + font-weight: 600; + text-transform: uppercase; +} + +/* Responsive design */ +@media (max-width: 768px) { + .poll-card { + padding: 12px; + } + + .poll-title { + font-size: 16px; + } + + .poll-meta { + flex-direction: column; + align-items: flex-start; + } + + .poll-time { + align-self: flex-end; + } +} \ No newline at end of file diff --git a/src/components/CSS/ProfileStyles.CSS b/src/components/CSS/ProfileStyles.CSS new file mode 100644 index 00000000..9c2291b3 --- /dev/null +++ b/src/components/CSS/ProfileStyles.CSS @@ -0,0 +1,133 @@ +.profile-page { + max-width: 800px; + margin: 0 auto; + padding: 20px; +} + +.profile-header { + display: flex; + gap: 30px; + align-items: flex-start; + background: white; + border-radius: 12px; + padding: 30px; + box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); + margin-bottom: 20px; +} + +.profile-picture { + flex-shrink: 0; +} + +.profile-img { + width: 150px; + height: 150px; + border-radius: 50%; + object-fit: cover; + border: 4px solid #f0f0f0; +} + +.profile-info { + flex: 1; +} + +.display-name { + font-size: 2rem; + font-weight: bold; + margin: 0 0 5px 0; + color: #333; +} + +.username { + font-size: 1.1rem; + color: #666; + margin: 0 0 20px 0; +} + +.stats { + display: flex; + gap: 30px; + margin-bottom: 20px; +} + +.stat-item { + display: flex; + flex-direction: column; + align-items: center; + text-align: center; +} + +.stat-count { + font-size: 1.5rem; + font-weight: bold; + color: #333; +} + +.stat-label { + font-size: 0.9rem; + color: #666; + margin-top: 2px; +} + +.bio { + font-size: 1rem; + color: #333; + line-height: 1.5; + margin-bottom: 20px; +} + +.profile-actions { + display: flex; + gap: 10px; +} + +.follow-btn, .message-btn { + padding: 10px 20px; + border: none; + border-radius: 6px; + font-weight: 600; + cursor: pointer; + transition: background-color 0.2s; +} + +.follow-btn { + background-color: #f0f0f0; + color: #333; + border: 1px solid #ddd; +} + +.follow-btn:hover { + background-color: #e0e0e0; +} + +.message-btn { + background-color: #f0f0f0; + color: #333; + border: 1px solid #ddd; +} + +.message-btn:hover { + background-color: #e0e0e0; +} + +/* Responsive design */ +@media (max-width: 768px) { + .profile-header { + flex-direction: column; + text-align: center; + padding: 20px; + } + + .profile-img { + width: 120px; + height: 120px; + } + + .display-name { + font-size: 1.8rem; + } + + .stats { + justify-content: center; + } +} \ No newline at end of file diff --git a/src/components/Dropdown.jsx b/src/components/Dropdown.jsx new file mode 100644 index 00000000..945cbec2 --- /dev/null +++ b/src/components/Dropdown.jsx @@ -0,0 +1,42 @@ +import React, { useState } from 'react'; +import { Link } from 'react-router-dom'; +import "./CSS/Dropdown.css"; +import "./CSS/NavBarStyles.css"; + +const Dropdown = () => { + const [open, setOpen] = useState(false); + + const handleMouseIn = () => setOpen(true); + const handleMouseOut = () => setOpen(false); + const handleClick =() => setOpen(false); + + const options = [ + { name: 'Create Poll', path: '/new-poll'}, + { name: 'Poll List', path: '/poll-list'}, + { name: 'Published Polls', path: '/'}, + { name: 'Drafted Polls', path: '/'}, + ]; + + return ( +
+ + {open && ( +
    + {options.map(({ name, path }) => ( +
  • + + {name} + +
  • + ))} +
+ )} +
+ ) +} + +export default Dropdown; diff --git a/src/components/Friends.jsx b/src/components/Friends.jsx new file mode 100644 index 00000000..e8e53d16 --- /dev/null +++ b/src/components/Friends.jsx @@ -0,0 +1,11 @@ +import React from "react"; + +const Friends = () => { + return ( + <> +

Friends Page!

+ + ); +}; + +export default Friends; diff --git a/src/components/FriendsPage.jsx b/src/components/FriendsPage.jsx new file mode 100644 index 00000000..0b887fdc --- /dev/null +++ b/src/components/FriendsPage.jsx @@ -0,0 +1,54 @@ +import React from "react"; +import "./css/Friends.css"; + +const FriendsPage = () => { + return ( +
+
+
+
+

Friend Name

+ +
+
+
+

Followers:

+

35

+
+
+

Following:

+

256

+
+
+
+
+
+

Pinned Polls:

+
+ + + +
+
+
+
+
+

Polls:

+
+ + + +
+
+ + + +
+
+
+
+
+ ); +}; + +export default FriendsPage; diff --git a/src/components/Login.jsx b/src/components/Login.jsx index 849e495a..dfab5d8f 100644 --- a/src/components/Login.jsx +++ b/src/components/Login.jsx @@ -2,7 +2,7 @@ import React, { useState } from "react"; import { useNavigate, Link } from "react-router-dom"; import axios from "axios"; import { API_URL } from "../shared"; -import "./AuthStyles.css"; +import "./CSS/AuthStyles.css"; const Login = ({ setUser }) => { const [formData, setFormData] = useState({ diff --git a/src/components/NavBar.jsx b/src/components/NavBar.jsx index 648f3808..74da5a74 100644 --- a/src/components/NavBar.jsx +++ b/src/components/NavBar.jsx @@ -1,24 +1,38 @@ import React from "react"; import { Link } from "react-router-dom"; -import "./NavBarStyles.css"; +import Dropdown from "./Dropdown"; +import "./CSS/NavBarStyles.css"; const NavBar = ({ user, onLogout }) => { return (