diff --git a/trivia-forge/backend/main.py b/trivia-forge/backend/main.py index 5ba4cc0d..1c16a16f 100644 --- a/trivia-forge/backend/main.py +++ b/trivia-forge/backend/main.py @@ -1,8 +1,10 @@ from flask import Flask, request, jsonify from endpoints import home, user, game, category, question, choice +from flask_cors import CORS app = Flask(__name__) +CORS(app) app.register_blueprint(home.bp) app.register_blueprint(user.bp) app.register_blueprint(game.bp) diff --git a/trivia-forge/frontend/src/App.css b/trivia-forge/frontend/src/App.css index 5b456753..591f1174 100644 --- a/trivia-forge/frontend/src/App.css +++ b/trivia-forge/frontend/src/App.css @@ -238,3 +238,10 @@ footer { flex: 0 0 auto; text-align: center; } + + +.card:hover { + box-shadow: 0 4px 8px 0 rgba(0,0,0,0.2); + /* border: solid; + border-color: darkgrey; */ +} \ No newline at end of file diff --git a/trivia-forge/frontend/src/Components/GameCategories.jsx b/trivia-forge/frontend/src/Components/GameCategories.jsx new file mode 100644 index 00000000..7c710021 --- /dev/null +++ b/trivia-forge/frontend/src/Components/GameCategories.jsx @@ -0,0 +1,24 @@ +import { React, useState, useEffect } from "react"; +import { getCategories } from "../Services/TF-db_services"; + +function GameCategories(game) { + const [categories, setCategories] = useState(null); + + useEffect(() => { + getCategories(game.data).then( res => { + setCategories(res); + }); + }, []); + + return ( + <> + {categories && ( + categories.map((category, index) => ( + • {category.title} + )) + )} + + ) +} + +export default GameCategories; \ No newline at end of file diff --git a/trivia-forge/frontend/src/Components/GameQuestions.jsx b/trivia-forge/frontend/src/Components/GameQuestions.jsx new file mode 100644 index 00000000..2be17240 --- /dev/null +++ b/trivia-forge/frontend/src/Components/GameQuestions.jsx @@ -0,0 +1,37 @@ +import { React, useState, useEffect } from "react"; +import { getCategories, getQuestions } from "../Services/TF-db_services"; + +function GameQuestions(game) { + const [categories, setCategories] = useState(null); + const [questions, setQuestions] = useState(null); + + + useEffect(() => { + getCategories(game.data).then( res => { + setCategories(res); + }); + }, []); + + useEffect(() => { + if (categories) { + const data = new Set(); + for (let i = 0; i < categories.length; i++) { + data.add(categories[i].id) + }; + + getQuestions(data).then( res => { + setQuestions(res); + }); + } + }, [categories]); + + return ( + <> + {questions && ( + {questions.length} + )} + + ) +} + +export default GameQuestions; \ No newline at end of file diff --git a/trivia-forge/frontend/src/Components/Questions.jsx b/trivia-forge/frontend/src/Components/Questions.jsx index 3daaf5cf..014e8da3 100644 --- a/trivia-forge/frontend/src/Components/Questions.jsx +++ b/trivia-forge/frontend/src/Components/Questions.jsx @@ -2,7 +2,7 @@ import React from "react"; import Choices from "../Components/Choices"; import { Card } from "react-bootstrap"; -import { Question } from "../Model/Question"; +import { Question } from "../Models/Question"; function Questions({ data }) { let choices = data.choices; diff --git a/trivia-forge/frontend/src/Components/Slideshow.jsx b/trivia-forge/frontend/src/Components/Slideshow.jsx new file mode 100644 index 00000000..85e36476 --- /dev/null +++ b/trivia-forge/frontend/src/Components/Slideshow.jsx @@ -0,0 +1,62 @@ +import { React, useState, useEffect } from "react"; +import Carousel from 'react-bootstrap/Carousel'; +import { getCategories, getQuestions ,getChoices } from "../Services/TF-db_services"; +const slideshowBackground = "https://yxdrsdfocuonvorowgaa.supabase.co/storage/v1/object/sign/UI%20Assets/white-solid-color-background?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1cmwiOiJVSSBBc3NldHMvd2hpdGUtc29saWQtY29sb3ItYmFja2dyb3VuZCIsImlhdCI6MTcxNTE3MDQ0NywiZXhwIjo0ODY4NzcwNDQ3fQ.dPaQP-yvK0-k6wBJWrI6FqrXGEqv6Vv-a8Th99zGSyA&t=2024-05-08T12%3A14%3A08.001Z" + + +function Slideshow(game) { + const [categories, setCategories] = useState(null); + const [questions, setQuestions] = useState(null); + const [choices, setChoices] = useState(null); + + useEffect(() => { + getCategories(game.data).then( res => { + setCategories(res); + }); + }, []); + + useEffect(() => { + if (categories) { + const data = new Set(); + for (let i = 0; i < categories.length; i++) { + data.add(categories[i].id) + }; + getQuestions(data).then( res => { + setQuestions(res); + }); + } + }, [categories]); + + // useEffect(() => { + // if (questions) { + // const data = new Set(); + // for (let i = 0; i < categories.length; i++) { + // data.add(categories[i].id) + // }; + // getQuestions(data).then( res => { + // setQuestions(res); + // }); + // } + // }, [questions]); + + + return ( + <> + {questions &&( + + {questions.map((q, index) => ( + + + +

{q.problem}

+
+
+ ))} +
+ )} + + ) + +} + +export default Slideshow; \ No newline at end of file diff --git a/trivia-forge/frontend/src/Model/Category.jsx b/trivia-forge/frontend/src/Models/Category.jsx similarity index 100% rename from trivia-forge/frontend/src/Model/Category.jsx rename to trivia-forge/frontend/src/Models/Category.jsx diff --git a/trivia-forge/frontend/src/Model/Choice.jsx b/trivia-forge/frontend/src/Models/Choice.jsx similarity index 100% rename from trivia-forge/frontend/src/Model/Choice.jsx rename to trivia-forge/frontend/src/Models/Choice.jsx diff --git a/trivia-forge/frontend/src/Model/Game.jsx b/trivia-forge/frontend/src/Models/Game.jsx similarity index 100% rename from trivia-forge/frontend/src/Model/Game.jsx rename to trivia-forge/frontend/src/Models/Game.jsx diff --git a/trivia-forge/frontend/src/Model/Question.jsx b/trivia-forge/frontend/src/Models/Question.jsx similarity index 100% rename from trivia-forge/frontend/src/Model/Question.jsx rename to trivia-forge/frontend/src/Models/Question.jsx diff --git a/trivia-forge/frontend/src/Model/User.jsx b/trivia-forge/frontend/src/Models/User.jsx similarity index 54% rename from trivia-forge/frontend/src/Model/User.jsx rename to trivia-forge/frontend/src/Models/User.jsx index 91896b1e..15d96c31 100644 --- a/trivia-forge/frontend/src/Model/User.jsx +++ b/trivia-forge/frontend/src/Models/User.jsx @@ -1,10 +1,11 @@ import datetime from 'node-datetime'; export class User { - constructor(date, email, password, profilePic = null) { - this.id = null; + constructor(date, email, password, username, profilePic = null) { + // this.id = null; + this.username = username; this.email = email; this.password = password; - this.profilePic = profilePic; + // this.profilePic = null; this.games = []; } @@ -13,10 +14,11 @@ export class User { } toJsonObject() { return { - id: this.id, + // id: this.id, + username: this.username, email: this.email, password: this.password, - profilePic: this.profilePic + // profilePic: this.profilePic } } } \ No newline at end of file diff --git a/trivia-forge/frontend/src/Pages/MyTrivia.jsx b/trivia-forge/frontend/src/Pages/MyTrivia.jsx index 740ca746..1a51170f 100644 --- a/trivia-forge/frontend/src/Pages/MyTrivia.jsx +++ b/trivia-forge/frontend/src/Pages/MyTrivia.jsx @@ -1,16 +1,96 @@ -import React from "react"; -import BootstrapTable from '../Components/BootstrapTable'; +import { React, useState, useEffect } from "react"; +import { getGames } from "../Services/TF-db_services"; +import Card from 'react-bootstrap/Card'; +import Col from 'react-bootstrap/Col'; +import Row from 'react-bootstrap/Row'; +import Button from 'react-bootstrap/Button'; +import GameCategories from "../Components/GameCategories"; +import GameQuestions from "../Components/GameQuestions"; +import Slideshow from "../Components/Slideshow"; +import Modal from 'react-bootstrap/Modal'; +// import BootstrapTable from '../Components/BootstrapTable'; function MyTrivia() { - return ( - <> -

- My Trivia Page test + const [games, setGames] = useState(null); + const [show, setShow] = useState(false); + const [currentGame, setCurrentGame] = useState(null) + + useEffect(() => { + getGames().then( res => { + setGames(res); + }); + }, []); + + useEffect(() => { + if(currentGame) { + setShow(true); + } + }, [currentGame]); + + function handleClose() { + setShow(false); + setCurrentGame(null); + } + + function handleShow(game) { + setCurrentGame(game); + } -

- + + return ( + <> + {games &&( + games.length > 0 ? ( + + {games.map((game, index) => ( + + + {game.title} + + Category: + + + + Number of Questions: + + + +
+ +
+
+
+ + ))} +
+ ) : ( +

No games to display.

+ ) + )} + + + + + + + + + ); } -export default MyTrivia; \ No newline at end of file +export default MyTrivia; +// function MyTrivia() { +// return ( +// <> +//

+// My Trivia Page test + +//

+// +// +// ); + +// } +// export default MyTrivia; \ No newline at end of file diff --git a/trivia-forge/frontend/src/Pages/SignUpPage.jsx b/trivia-forge/frontend/src/Pages/SignUpPage.jsx index 29ff7bba..3159b317 100644 --- a/trivia-forge/frontend/src/Pages/SignUpPage.jsx +++ b/trivia-forge/frontend/src/Pages/SignUpPage.jsx @@ -2,11 +2,13 @@ import React, { useState } from 'react'; import { useNavigate } from "react-router-dom"; import { Form, Button, Card } from "react-bootstrap"; import { addUser } from '../Services/TF-db_services'; +import { User } from '../Models/User'; function SignUpPage() { const [email, setEmail] = useState(''); const [password, setPassword] = useState(''); const [confirmPassword, setConfirmPassword] = useState(''); + const [username, setUsername] = useState(''); const navigate = useNavigate(); const handleSubmit = async (event) => { @@ -15,10 +17,8 @@ function SignUpPage() { alert('Passwords do not match'); return; } - const user = { - email: email, - password: password - }; + const user = new User(null, email, password, username); + console.log('Sending user data:', user.toJsonObject()); const addedUser = await addUser(user); if (addedUser) { alert('User added:', addedUser); @@ -40,6 +40,11 @@ function SignUpPage() { + + Username + setUsername(e.target.value)} /> + + Password setPassword(e.target.value)} /> diff --git a/trivia-forge/frontend/src/Pages/TriviaGenPage.jsx b/trivia-forge/frontend/src/Pages/TriviaGenPage.jsx index a68db140..815cd06d 100644 --- a/trivia-forge/frontend/src/Pages/TriviaGenPage.jsx +++ b/trivia-forge/frontend/src/Pages/TriviaGenPage.jsx @@ -1,10 +1,10 @@ import React, { useState } from "react"; // variables that cause the component to re-render when they change import OpenAI from "openai"; -import { Game } from "../Model/Game"; +import { Game } from "../Models/Game"; import { useNavigate } from "react-router-dom"; -import { Question } from "../Model/Question"; -import { Choice } from "../Model/Choice"; -import { Category } from "../Model/Category"; +import { Question } from "../Models/Question"; +import { Choice } from "../Models/Choice"; +import { Category } from "../Models/Category"; import { Card } from "react-bootstrap"; diff --git a/trivia-forge/frontend/src/Services/TF-db_services.jsx b/trivia-forge/frontend/src/Services/TF-db_services.jsx index c33dbb9e..223cdfd3 100644 --- a/trivia-forge/frontend/src/Services/TF-db_services.jsx +++ b/trivia-forge/frontend/src/Services/TF-db_services.jsx @@ -1,17 +1,18 @@ import axios from 'axios'; -import User from '../Model/User'; -import Game from '../Model/Game'; -import Question from '../Model/Question'; +import {User} from '../Models/User'; +import {Game} from '../Models/Game'; +import {Question} from '../Models/Question'; -const API_URL = 'http://localhost:5000/api'; + +const API_URL = 'http://localhost:5000'; /* ************************************ User ************************************ */ export const getUser = async () => { try { const response = await axios.get(`${API_URL}/users`); - const { id, date, email, password, profilePic } = response.data; - return new User(id, date, email, password, profilePic); + const { id, username, date, email, password, profilePic } = response.data; + return new User(id, username, date, email, password, profilePic); } catch (error) { console.error('Failed to fetch user'); return []; @@ -52,9 +53,19 @@ export const updateUser = async (user) => { /* ************************************ Game ************************************ */ -export const getGame = async () => { +export const getGames = async () => { try { const response = await axios.get(`${API_URL}/games`); + return response.data; + } catch (error) { + console.error('Failed to fetch games'); + return []; + } +} + +export const getGame = async (game) => { + try { + const response = await axios.get(`${API_URL}/games/${game.id}`); const { id, date, name, userID, questions } = response.data; return new Game(id, date, name, userID, questions); } catch (error) { @@ -96,13 +107,30 @@ export const updateGame = async (game) => { /* ************************************************************************************ */ /* ************************************ Questions ************************************** */ -export const getQuestions = async () => { +export const getQuestions = async (category_ids) => { try { const response = await axios.get(`${API_URL}/questions`); + let questions = [] + for (let i = 0; i < response.data.length; i++) { + if (category_ids.has(response.data[i].category_id)) { + questions.push(response.data[i]); + }; + }; + return questions; + } catch (error) { + console.error('Failed to fetch questions'); + return []; + } + +}; + +export const getQuestion = async (question) => { + try { + const response = await axios.get(`${API_URL}/questions${question.id}`); const { id, question, answer, categoryID } = response.data; return new Question(id, question, answer, categoryID); } catch (error) { - console.error('Failed to fetch questions'); + console.error('Failed to fetch question'); return []; } @@ -110,7 +138,7 @@ export const getQuestions = async () => { export const addQuestion = async (question) => { try { - const response = await axios.post(`${API_URL}/questions`, question.toJsonObject()); + const response = await axios.post(`${API_URL}/questions/${question.id}`, question.toJsonObject()); return response.data; } catch (error) { console.error('Failed to add question'); @@ -140,16 +168,33 @@ export const updateQuestion = async (question) => { /* ************************************************************************************ */ /* ************************************ Categories ************************************ */ -export const getCategories = async () => { +export const getCategories = async (game) => { try { const response = await axios.get(`${API_URL}/categories`); - return response.data; + let categories = [] + for (let i = 0; i < response.data.length; i++) { + if (response.data[i].game_id == game.id) { + categories.push(response.data[i]); + } + } + return categories; } catch (error) { console.error('Failed to fetch categories'); return []; } }; + +export const getCategory = async (category) => { + try { + const response = await axios.get(`${API_URL}/category/${category.id}`); + return response.data; + } catch (error) { + console.error('Failed to fetch category'); + return []; + } +}; + export const addCategory = async (category) => { try { const response = await axios.post(`${API_URL}/categories`, category.toJsonObject()); @@ -184,10 +229,20 @@ export const updateCategory = async (category) => { /* ************************************ Choice ************************************ */ -export const getChoice = async () => { +export const getChoices = async () => { try { const response = await axios.get(`${API_URL}/choices`); return response.data; + } catch (error) { + console.error('Failed to fetch choices'); + return []; + } +} + +export const getChoice = async (choice) => { + try { + const response = await axios.get(`${API_URL}/choices/${choice.id}`); + return response.data; } catch (error) { console.error('Failed to fetch choice'); return [];