diff --git a/paras/package-lock.json b/paras/package-lock.json
index 09405fc..0c24e8c 100644
--- a/paras/package-lock.json
+++ b/paras/package-lock.json
@@ -13,6 +13,7 @@
"@testing-library/user-event": "^14.5.2",
"react": "^18.3.1",
"react-dom": "^18.3.1",
+ "react-router-dom": "^6.27.0",
"react-scripts": "^5.0.1",
"web-vitals": "^4.2.3"
}
@@ -3183,6 +3184,15 @@
}
}
},
+ "node_modules/@remix-run/router": {
+ "version": "1.20.0",
+ "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.20.0.tgz",
+ "integrity": "sha512-mUnk8rPJBI9loFDZ+YzPGdeniYK+FTmRD1TMCz7ev2SNIozyKKpnGgsxO34u6Z4z/t0ITuu7voi/AshfsGsgFg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
"node_modules/@rollup/plugin-babel": {
"version": "5.3.1",
"resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.1.tgz",
@@ -13919,6 +13929,38 @@
"node": ">=0.10.0"
}
},
+ "node_modules/react-router": {
+ "version": "6.27.0",
+ "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.27.0.tgz",
+ "integrity": "sha512-YA+HGZXz4jaAkVoYBE98VQl+nVzI+cVI2Oj/06F5ZM+0u3TgedN9Y9kmMRo2mnkSK2nCpNQn0DVob4HCsY/WLw==",
+ "license": "MIT",
+ "dependencies": {
+ "@remix-run/router": "1.20.0"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ },
+ "peerDependencies": {
+ "react": ">=16.8"
+ }
+ },
+ "node_modules/react-router-dom": {
+ "version": "6.27.0",
+ "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.27.0.tgz",
+ "integrity": "sha512-+bvtFWMC0DgAFrfKXKG9Fc+BcXWRUO1aJIihbB79xaeq0v5UzfvnM5houGUm1Y461WVRcgAQ+Clh5rdb1eCx4g==",
+ "license": "MIT",
+ "dependencies": {
+ "@remix-run/router": "1.20.0",
+ "react-router": "6.27.0"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ },
+ "peerDependencies": {
+ "react": ">=16.8",
+ "react-dom": ">=16.8"
+ }
+ },
"node_modules/react-scripts": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/react-scripts/-/react-scripts-5.0.1.tgz",
diff --git a/paras/package.json b/paras/package.json
index bfcdd66..26c3a95 100644
--- a/paras/package.json
+++ b/paras/package.json
@@ -8,6 +8,7 @@
"@testing-library/user-event": "^14.5.2",
"react": "^18.3.1",
"react-dom": "^18.3.1",
+ "react-router-dom": "^6.27.0",
"react-scripts": "^5.0.1",
"web-vitals": "^4.2.3"
},
diff --git a/paras/src/App.css b/paras/src/App.css
index 290af52..f01d7d8 100644
--- a/paras/src/App.css
+++ b/paras/src/App.css
@@ -21,7 +21,7 @@ body {
justify-content: space-between;
align-items: center;
- padding: 10px 20px; /* Adjusted padding for better spacing */
+ padding: 40px 20px; /* Adjusted padding for better spacing */
background: rgb(204, 171, 234); /* Gradient background */
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.2);
border-radius: 8px; /* Rounded corners for a softer look */
diff --git a/paras/src/App.js b/paras/src/App.js
index 800d2a4..55cf855 100644
--- a/paras/src/App.js
+++ b/paras/src/App.js
@@ -1,190 +1,19 @@
-import React, { useState, useEffect } from 'react';
-import './App.css';
-import Sparkle from './Sparkle'; // If Sparkle is not used, you can remove this import.
-
-const initialBoard = Array(9).fill(null);
-
-const App = () => {
- const [board, setBoard] = useState(initialBoard);
- const [currentPlayer, setCurrentPlayer] = useState('X');
- const [winner, setWinner] = useState(null);
- const [draw, setDraw] = useState(false);
- const [theme, setTheme] = useState('system');
- const [isDarkMode, setIsDarkMode] = useState(false);
- const [scorePlayerX, setScorePlayerX] = useState(0);
- const [scorePlayerO, setScorePlayerO] = useState(0);
- const [highestScorePlayerX, setHighestScorePlayerX] = useState(0);
- const [highestScorePlayerO, setHighestScorePlayerO] = useState(0);
- const [gameMode, setGameMode] = useState(null);
-
- // Handle click on a cell
- const handleCellClick = (index) => {
- if (board[index] || winner || draw) return;
-
- const newBoard = [...board];
- newBoard[index] = currentPlayer;
- setBoard(newBoard);
- checkWinner(newBoard, currentPlayer);
- setCurrentPlayer(currentPlayer === 'X' ? 'O' : 'X');
- };
-
- // Check if there's a winner
- const checkWinner = (board, player) => {
- const winningCombinations = [
- [0, 1, 2], [3, 4, 5], [6, 7, 8],
- [0, 3, 6], [1, 4, 7], [2, 5, 8],
- [0, 4, 8], [2, 4, 6]
- ];
- for (let combination of winningCombinations) {
- const [a, b, c] = combination;
- if (board[a] === player && board[b] === player && board[c] === player) {
- setWinner(player);
- updateScoreAndHighestScore(player);
- return;
- }
- }
- if (board.every(cell => cell !== null) && !winner) {
- setDraw(true);
- }
- };
-
- // Update score and highest score
- const updateScoreAndHighestScore = (player) => {
- if (player === 'X') {
- const newScoreX = scorePlayerX + 1;
- setScorePlayerX(newScoreX);
- if (newScoreX > highestScorePlayerX) {
- setHighestScorePlayerX(newScoreX);
- }
- } else if (player === 'O') {
- const newScoreO = scorePlayerO + 1;
- setScorePlayerO(newScoreO);
- if (newScoreO > highestScorePlayerO) {
- setHighestScorePlayerO(newScoreO);
- }
- }
- };
-
- // Reset the game but keep the scores intact
- const resetGame = () => {
- setBoard(initialBoard);
- setCurrentPlayer('X');
- setWinner(null);
- setDraw(false);
- };
-
- // Handle theme changes
- const handleThemeChange = (selectedTheme) => {
- setTheme(selectedTheme);
- localStorage.setItem('theme', selectedTheme);
- applyTheme(selectedTheme);
- };
-
- const applyTheme = (selectedTheme) => {
- if (selectedTheme === 'dark') {
- setIsDarkMode(true);
- } else if (selectedTheme === 'light') {
- setIsDarkMode(false);
- } else {
- const systemDarkMode = window.matchMedia('(prefers-color-scheme: dark)').matches;
- setIsDarkMode(systemDarkMode);
- }
- };
-
- // Setup initial theme
- useEffect(() => {
- const savedTheme = localStorage.getItem('theme') || 'system';
- setTheme(savedTheme);
- applyTheme(savedTheme);
- }, []);
-
- // Render each cell
- const renderCell = (index) => {
- const value = board[index];
- return (
-
handleCellClick(index)}>
- {value}
-
- );
- };
-
- // Game Mode selection
- const handleBackButton = () => {
- setGameMode(null);
- resetGame();
- };
-
- if (gameMode === null) {
- return (
-
-
-
-
-
-
-
Tic Tac Toe
-
-
-
-
-
-
-
-
-
-
-
Choose Game Mode
-
-
-
-
- );
- }
-
+import React from "react";
+import { BrowserRouter as Router, Routes, Route } from "react-router-dom";
+import Game from "./Game"
+import Signup from "./Signup"
+import Login from "./Login"
+function App() {
return (
-
-
-
Tic Tac Toe
-
-
-
-
- X Wins: {scorePlayerX}
-
-
- O Wins: {scorePlayerO}
-
-
-
-
-
-
- {board.map((cell, index) => renderCell(index))}
-
-
- {winner && (
-
-
Player {winner} wins!
-
-
- )}
-
+
+
+ } />
+ } />
+ } />
+
+
+
);
-};
+}
export default App;
diff --git a/paras/src/Game.js b/paras/src/Game.js
new file mode 100644
index 0000000..b56cfeb
--- /dev/null
+++ b/paras/src/Game.js
@@ -0,0 +1,192 @@
+import React, { useState, useEffect } from 'react';
+import './App.css';
+import Sparkle from './Sparkle'; // If Sparkle is not used, you can remove this import.
+import { Link } from 'react-router-dom';
+const initialBoard = Array(9).fill(null);
+
+const App = () => {
+ const [board, setBoard] = useState(initialBoard);
+ const [currentPlayer, setCurrentPlayer] = useState('X');
+ const [winner, setWinner] = useState(null);
+ const [draw, setDraw] = useState(false);
+ const [theme, setTheme] = useState('system');
+ const [isDarkMode, setIsDarkMode] = useState(false);
+ const [scorePlayerX, setScorePlayerX] = useState(0);
+ const [scorePlayerO, setScorePlayerO] = useState(0);
+ const [highestScorePlayerX, setHighestScorePlayerX] = useState(0);
+ const [highestScorePlayerO, setHighestScorePlayerO] = useState(0);
+ const [gameMode, setGameMode] = useState(null);
+
+ // Handle click on a cell
+ const handleCellClick = (index) => {
+ if (board[index] || winner || draw) return;
+
+ const newBoard = [...board];
+ newBoard[index] = currentPlayer;
+ setBoard(newBoard);
+ checkWinner(newBoard, currentPlayer);
+ setCurrentPlayer(currentPlayer === 'X' ? 'O' : 'X');
+ };
+
+ // Check if there's a winner
+ const checkWinner = (board, player) => {
+ const winningCombinations = [
+ [0, 1, 2], [3, 4, 5], [6, 7, 8],
+ [0, 3, 6], [1, 4, 7], [2, 5, 8],
+ [0, 4, 8], [2, 4, 6]
+ ];
+ for (let combination of winningCombinations) {
+ const [a, b, c] = combination;
+ if (board[a] === player && board[b] === player && board[c] === player) {
+ setWinner(player);
+ updateScoreAndHighestScore(player);
+ return;
+ }
+ }
+ if (board.every(cell => cell !== null) && !winner) {
+ setDraw(true);
+ }
+ };
+
+ // Update score and highest score
+ const updateScoreAndHighestScore = (player) => {
+ if (player === 'X') {
+ const newScoreX = scorePlayerX + 1;
+ setScorePlayerX(newScoreX);
+ if (newScoreX > highestScorePlayerX) {
+ setHighestScorePlayerX(newScoreX);
+ }
+ } else if (player === 'O') {
+ const newScoreO = scorePlayerO + 1;
+ setScorePlayerO(newScoreO);
+ if (newScoreO > highestScorePlayerO) {
+ setHighestScorePlayerO(newScoreO);
+ }
+ }
+ };
+
+ // Reset the game but keep the scores intact
+ const resetGame = () => {
+ setBoard(initialBoard);
+ setCurrentPlayer('X');
+ setWinner(null);
+ setDraw(false);
+ };
+
+ // Handle theme changes
+ const handleThemeChange = (selectedTheme) => {
+ setTheme(selectedTheme);
+ localStorage.setItem('theme', selectedTheme);
+ applyTheme(selectedTheme);
+ };
+
+ const applyTheme = (selectedTheme) => {
+ if (selectedTheme === 'dark') {
+ setIsDarkMode(true);
+ } else if (selectedTheme === 'light') {
+ setIsDarkMode(false);
+ } else {
+ const systemDarkMode = window.matchMedia('(prefers-color-scheme: dark)').matches;
+ setIsDarkMode(systemDarkMode);
+ }
+ };
+
+ // Setup initial theme
+ useEffect(() => {
+ const savedTheme = localStorage.getItem('theme') || 'system';
+ setTheme(savedTheme);
+ applyTheme(savedTheme);
+ }, []);
+
+ // Render each cell
+ const renderCell = (index) => {
+ const value = board[index];
+ return (
+ handleCellClick(index)}>
+ {value}
+
+ );
+ };
+
+ // Game Mode selection
+ const handleBackButton = () => {
+ setGameMode(null);
+ resetGame();
+ };
+
+ if (gameMode === null) {
+ return (
+
+
+
+
+
+
+
Tic Tac Toe
+
+
+
+ Home
+ About
+ Rules
+ Contact
+ Sign Up
+ Log In
+
+
+
+
+
+
+
+
+
+
+
Choose Game Mode
+
+
+
+
+ );
+ }
+
+ return (
+
+
+
Tic Tac Toe
+
+
+
+
+ X Wins: {scorePlayerX}
+
+
+ O Wins: {scorePlayerO}
+
+
+
+
+
+
+ {board.map((cell, index) => renderCell(index))}
+
+
+ {winner && (
+
+
Player {winner} wins!
+
+
+ )}
+
+ );
+};
+
+export default App;
diff --git a/paras/src/Login.css b/paras/src/Login.css
new file mode 100644
index 0000000..947f08d
--- /dev/null
+++ b/paras/src/Login.css
@@ -0,0 +1,95 @@
+* {
+ box-sizing: border-box;
+ margin: 0;
+ padding: 0;
+}
+
+body {
+ font-family: Arial, sans-serif;
+ background-color: #f3f4f6;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ height: 100vh;
+}
+
+.login-container {
+ background-color: #fff;
+ padding: 30px;
+ border-radius: 10px;
+ box-shadow: 0 0 15px rgba(0, 0, 0, 0.1);
+ width: 100%;
+ max-width: 400px;
+ text-align: center;
+}
+
+.login-container h2 {
+ margin-bottom: 20px;
+ color: #333;
+}
+
+.login-form {
+ display: flex;
+ flex-direction: column;
+}
+
+.form-group {
+ margin-bottom: 15px;
+ text-align: left;
+}
+
+.form-group label {
+ font-size: 14px;
+ color: #666;
+}
+
+.form-group input {
+ padding: 10px;
+ width: 100%;
+ font-size: 16px;
+ border: 1px solid #ccc;
+ border-radius: 5px;
+ margin-top: 5px;
+}
+
+.form-group input:focus {
+ outline: none;
+ border-color: #007bff;
+}
+
+.login-btn {
+ padding: 12px;
+ background-color: #007bff;
+ color: #fff;
+ border: none;
+ border-radius: 5px;
+ cursor: pointer;
+ font-size: 16px;
+}
+
+.login-btn:hover {
+ background-color: #0056b3;
+}
+
+.error-msg {
+ color: red;
+ font-size: 12px;
+ margin-top: 5px;
+}
+
+.success-msg {
+ color: green;
+ font-size: 14px;
+ margin-bottom: 15px;
+}
+
+.error-input {
+ border-color: red;
+}
+
+.login-container {
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+}
diff --git a/paras/src/Login.js b/paras/src/Login.js
new file mode 100644
index 0000000..f940faa
--- /dev/null
+++ b/paras/src/Login.js
@@ -0,0 +1,106 @@
+import React, { useState } from "react";
+import { Link, useNavigate } from "react-router-dom";
+import "./Login.css";
+
+const Login = () => {
+ const [formData, setFormData] = useState({
+ email: "",
+ password: "",
+ });
+
+ const [errors, setErrors] = useState({});
+ const [submitted, setSubmitted] = useState(false);
+ const navigate = useNavigate();
+
+ const handleChange = (e) => {
+ const { name, value } = e.target;
+ setFormData({
+ ...formData,
+ [name]: value,
+ });
+ };
+
+ const validate = () => {
+ let formErrors = {};
+ const storedUser = JSON.parse(localStorage.getItem("user"));
+
+ if (!formData.email) {
+ formErrors.email = "Email is required";
+ } else if (!/\S+@\S+\.\S+/.test(formData.email)) {
+ formErrors.email = "Email address is invalid";
+ }
+
+ if (!formData.password) {
+ formErrors.password = "Password is required";
+ }
+
+ if (storedUser) {
+ if (formData.email !== storedUser.email || formData.password !== storedUser.password) {
+ formErrors.credentials = "Invalid email or password";
+ }
+ } else {
+ formErrors.credentials = "No account found. Please sign up.";
+ }
+
+ return formErrors;
+ };
+
+ const handleSubmit = (e) => {
+ e.preventDefault();
+ const formErrors = validate();
+ if (Object.keys(formErrors).length === 0) {
+ setSubmitted(true);
+ setErrors({});
+ // Redirect to the dashboard or home page on successful login
+ setTimeout(() => {
+ navigate("/");
+ }, 2000);
+ } else {
+ setErrors(formErrors);
+ setSubmitted(false);
+ }
+ };
+
+ return (
+
+
Login
+ {submitted &&
Login successful! Redirecting...
}
+
+
+ Don't have an account?{" "}
+ Sign Up
+
+
+ );
+};
+
+export default Login;
diff --git a/paras/src/Signup.css b/paras/src/Signup.css
new file mode 100644
index 0000000..491428a
--- /dev/null
+++ b/paras/src/Signup.css
@@ -0,0 +1,110 @@
+* {
+ box-sizing: border-box;
+ margin: 0;
+ padding: 0;
+}
+
+body {
+ font-family: Arial, sans-serif;
+ background-color: #f3f4f6;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ height: 100vh;
+}
+
+.signup-container {
+ background-color: #fff;
+ padding: 30px;
+ border-radius: 10px;
+ box-shadow: 0 0 15px rgba(0, 0, 0, 0.1);
+ width: 100%;
+ max-width: 400px;
+ text-align: center;
+}
+
+.signup-container h2 {
+ margin-bottom: 20px;
+ color: #333;
+}
+
+.signup-form {
+ display: flex;
+ flex-direction: column;
+}
+
+.form-group {
+ margin-bottom: 15px;
+ text-align: left;
+}
+
+.form-group label {
+ font-size: 14px;
+ color: #666;
+}
+
+.form-group input {
+ padding: 10px;
+ width: 100%;
+ font-size: 16px;
+ border: 1px solid #ccc;
+ border-radius: 5px;
+ margin-top: 5px;
+}
+
+.form-group input:focus {
+ outline: none;
+ border-color: #007bff;
+}
+
+.signup-btn {
+ padding: 12px;
+ background-color: #007bff;
+ color: #fff;
+ border: none;
+ border-radius: 5px;
+ cursor: pointer;
+ font-size: 16px;
+}
+
+.signup-btn:hover {
+ background-color: #0056b3;
+}
+
+.error-msg {
+ color: red;
+ font-size: 12px;
+ margin-top: 5px;
+}
+
+.success-msg {
+ color: green;
+ font-size: 14px;
+ margin-bottom: 15px;
+}
+
+.error-input {
+ border-color: red;
+}
+
+.signup-container {
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+}
+
+.redirect-text {
+ margin-top: 20px;
+ font-size: 14px;
+ color: #333;
+}
+
+.redirect-link {
+ color: #007bff;
+ text-decoration: none;
+}
+
+.redirect-link:hover {
+ text-decoration: underline;
+}
diff --git a/paras/src/Signup.js b/paras/src/Signup.js
new file mode 100644
index 0000000..fcbfd6b
--- /dev/null
+++ b/paras/src/Signup.js
@@ -0,0 +1,131 @@
+import React, { useState } from "react";
+import { Link, useNavigate } from "react-router-dom";
+import "./Signup.css";
+
+const Signup = () => {
+ const [formData, setFormData] = useState({
+ username: "",
+ email: "",
+ password: "",
+ confirmPassword: "",
+ });
+
+ const [errors, setErrors] = useState({});
+ const [submitted, setSubmitted] = useState(false);
+ const navigate = useNavigate();
+
+ const handleChange = (e) => {
+ const { name, value } = e.target;
+ setFormData({
+ ...formData,
+ [name]: value,
+ });
+ };
+
+ const validate = () => {
+ let formErrors = {};
+ if (!formData.username) formErrors.username = "Username is required";
+ if (!formData.email) {
+ formErrors.email = "Email is required";
+ } else if (!/\S+@\S+\.\S+/.test(formData.email)) {
+ formErrors.email = "Email address is invalid";
+ }
+ if (!formData.password) formErrors.password = "Password is required";
+ if (formData.password.length < 6)
+ formErrors.password = "Password must be at least 6 characters";
+
+ if (!formData.confirmPassword) {
+ formErrors.confirmPassword = "Please confirm your password";
+ } else if (formData.password !== formData.confirmPassword) {
+ formErrors.confirmPassword = "Passwords do not match";
+ }
+
+ return formErrors;
+ };
+
+ const handleSubmit = (e) => {
+ e.preventDefault();
+ const formErrors = validate();
+ if (Object.keys(formErrors).length === 0) {
+ setSubmitted(true);
+ setErrors({});
+ // Save user data to localStorage
+ localStorage.setItem("user", JSON.stringify(formData));
+ // Redirect to login page after successful sign-up
+ setTimeout(() => {
+ navigate("/login");
+ }, 2000);
+ } else {
+ setErrors(formErrors);
+ setSubmitted(false);
+ }
+ };
+
+ return (
+
+
Sign Up
+ {submitted &&
Sign-up successful! Redirecting to login...
}
+
+
+ Already have an account?{" "}
+ Log In
+
+
+ );
+};
+
+export default Signup;