diff --git a/index.html b/index.html index 0c589ec..7f2abc5 100644 --- a/index.html +++ b/index.html @@ -2,7 +2,7 @@ - + Vite + React diff --git a/package-lock.json b/package-lock.json index 64a96e2..3384e4f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,7 +15,8 @@ "axios": "^1.7.9", "react": "^18.3.1", "react-dom": "^18.3.1", - "react-redux": "^9.2.0" + "react-redux": "^9.2.0", + "react-router-dom": "^7.1.3" }, "devDependencies": { "@eslint/js": "^9.17.0", @@ -1721,6 +1722,12 @@ "@babel/types": "^7.20.7" } }, + "node_modules/@types/cookie": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.6.0.tgz", + "integrity": "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==", + "license": "MIT" + }, "node_modules/@types/estree": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", @@ -2282,6 +2289,15 @@ "dev": true, "license": "MIT" }, + "node_modules/cookie": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.0.2.tgz", + "integrity": "sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/cosmiconfig": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", @@ -4453,6 +4469,46 @@ "node": ">=0.10.0" } }, + "node_modules/react-router": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.1.3.tgz", + "integrity": "sha512-EezYymLY6Guk/zLQ2vRA8WvdUhWFEj5fcE3RfWihhxXBW7+cd1LsIiA3lmx+KCmneAGQuyBv820o44L2+TtkSA==", + "license": "MIT", + "dependencies": { + "@types/cookie": "^0.6.0", + "cookie": "^1.0.1", + "set-cookie-parser": "^2.6.0", + "turbo-stream": "2.4.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + } + } + }, + "node_modules/react-router-dom": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.1.3.tgz", + "integrity": "sha512-qQGTE+77hleBzv9SIUIkGRvuFBQGagW+TQKy53UTZAO/3+YFNBYvRsNIZ1GT17yHbc63FylMOdS+m3oUriF1GA==", + "license": "MIT", + "dependencies": { + "react-router": "7.1.3" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18" + } + }, "node_modules/react-transition-group": { "version": "4.4.5", "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", @@ -4680,6 +4736,12 @@ "semver": "bin/semver.js" } }, + "node_modules/set-cookie-parser": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.1.tgz", + "integrity": "sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==", + "license": "MIT" + }, "node_modules/set-function-length": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", @@ -4989,6 +5051,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/turbo-stream": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/turbo-stream/-/turbo-stream-2.4.0.tgz", + "integrity": "sha512-FHncC10WpBd2eOmGwpmQsWLDoK4cqsA/UT/GqNoaKOQnT8uzhtCbg3EoUDMvqpOSAI0S26mr0rkjzbOO6S3v1g==", + "license": "ISC" + }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", diff --git a/package.json b/package.json index 20a3a40..e8c9537 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,8 @@ "axios": "^1.7.9", "react": "^18.3.1", "react-dom": "^18.3.1", - "react-redux": "^9.2.0" + "react-redux": "^9.2.0", + "react-router-dom": "^7.1.3" }, "devDependencies": { "@eslint/js": "^9.17.0", diff --git a/src/App.css b/src/App.css index b9d355d..e69de29 100644 --- a/src/App.css +++ b/src/App.css @@ -1,42 +0,0 @@ -#root { - max-width: 1280px; - margin: 0 auto; - padding: 2rem; - text-align: center; -} - -.logo { - height: 6em; - padding: 1.5em; - will-change: filter; - transition: filter 300ms; -} -.logo:hover { - filter: drop-shadow(0 0 2em #646cffaa); -} -.logo.react:hover { - filter: drop-shadow(0 0 2em #61dafbaa); -} - -@keyframes logo-spin { - from { - transform: rotate(0deg); - } - to { - transform: rotate(360deg); - } -} - -@media (prefers-reduced-motion: no-preference) { - a:nth-of-type(2) .logo { - animation: logo-spin infinite 20s linear; - } -} - -.card { - padding: 2em; -} - -.read-the-docs { - color: #888; -} diff --git a/src/App.jsx b/src/App.jsx index f67355a..2a7ff79 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -1,35 +1,41 @@ -import { useState } from 'react' -import reactLogo from './assets/react.svg' -import viteLogo from '/vite.svg' -import './App.css' - -function App() { - const [count, setCount] = useState(0) +import React, { useState } from 'react'; +import { BrowserRouter as Router, Routes, Route } from 'react-router-dom'; +import { Provider } from 'react-redux'; +import store from './app/store'; +import LoginPage from './pages/LoginPage'; +import JobDescriptionPage from './pages/JobDescriptionPage'; +import InterviewQuestionsPage from './pages/InterviewQuestionsPage'; +import ResourceInvitePage from './pages/ResourceInvitePage'; +import { ThemeProvider, CssBaseline, Button } from '@mui/material'; +import { darkTheme, lightTheme } from './theme'; +const App = () => { + const [darkMode, setDarkMode] = useState(false); + const toggleDarkMode = () => setDarkMode(!darkMode); + return ( - <> -
- - Vite logo - - - React logo - -
-

Vite + React

-
- -

- Edit src/App.jsx and save to test HMR -

-
-

- Click on the Vite and React logos to learn more -

- - ) -} + + + + + + + } /> + } /> + } /> + } /> + } /> + + + + + ); +}; -export default App +export default App; diff --git a/src/app/store.js b/src/app/store.js new file mode 100644 index 0000000..3c23ff5 --- /dev/null +++ b/src/app/store.js @@ -0,0 +1,12 @@ +import { configureStore } from '@reduxjs/toolkit'; +import authReducer from '../features/auth/authSlice'; +// import jobReducer from '../features/job/jobSlice'; + +const store = configureStore({ + reducer: { + auth: authReducer, + // job: jobReducer, + }, +}); + +export default store; diff --git a/src/assets/favicon.jpg b/src/assets/favicon.jpg new file mode 100644 index 0000000..6056ca3 Binary files /dev/null and b/src/assets/favicon.jpg differ diff --git a/src/features/auth/authSlice.js b/src/features/auth/authSlice.js new file mode 100644 index 0000000..33ded3a --- /dev/null +++ b/src/features/auth/authSlice.js @@ -0,0 +1,28 @@ +import { createSlice } from '@reduxjs/toolkit'; + +const initialState = { + user: null, + isAuthenticated: false, + error: null, +}; + +const authSlice = createSlice({ + name: 'auth', + initialState, + reducers: { + loginSuccess: (state, action) => { + state.user = action.payload; + state.isAuthenticated = true; + }, + logout: (state) => { + state.user = null; + state.isAuthenticated = false; + }, + setError: (state, action) => { + state.error = action.payload; + }, + }, +}); + +export const { loginSuccess, logout, setError } = authSlice.actions; +export default authSlice.reducer; diff --git a/src/helper/helper.js b/src/helper/helper.js new file mode 100644 index 0000000..995424d --- /dev/null +++ b/src/helper/helper.js @@ -0,0 +1,9 @@ +// src/ +// ├── components/ # Reusable UI components +// ├── features/ # Redux slices and related logic +// ├── pages/ # Page components for routes +// ├── app/ # Redux store setup +// ├── services/ # API calls and services +// ├── utils/ # Utility functions +// ├── App.jsx # Main App component +// └── main.jsx # Entry point \ No newline at end of file diff --git a/src/index.css b/src/index.css index 6119ad9..e69de29 100644 --- a/src/index.css +++ b/src/index.css @@ -1,68 +0,0 @@ -:root { - font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif; - line-height: 1.5; - font-weight: 400; - - color-scheme: light dark; - color: rgba(255, 255, 255, 0.87); - background-color: #242424; - - font-synthesis: none; - text-rendering: optimizeLegibility; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; -} - -a { - font-weight: 500; - color: #646cff; - text-decoration: inherit; -} -a:hover { - color: #535bf2; -} - -body { - margin: 0; - display: flex; - place-items: center; - min-width: 320px; - min-height: 100vh; -} - -h1 { - font-size: 3.2em; - line-height: 1.1; -} - -button { - border-radius: 8px; - border: 1px solid transparent; - padding: 0.6em 1.2em; - font-size: 1em; - font-weight: 500; - font-family: inherit; - background-color: #1a1a1a; - cursor: pointer; - transition: border-color 0.25s; -} -button:hover { - border-color: #646cff; -} -button:focus, -button:focus-visible { - outline: 4px auto -webkit-focus-ring-color; -} - -@media (prefers-color-scheme: light) { - :root { - color: #213547; - background-color: #ffffff; - } - a:hover { - color: #747bff; - } - button { - background-color: #f9f9f9; - } -} diff --git a/src/pages/InterviewQuestionsPage.jsx b/src/pages/InterviewQuestionsPage.jsx new file mode 100644 index 0000000..6a7c7cc --- /dev/null +++ b/src/pages/InterviewQuestionsPage.jsx @@ -0,0 +1,8 @@ +function InterviewQuestionsPage(params) { + return( +

+ InterviewQuestionsPage

+ ) +} + +export default InterviewQuestionsPage \ No newline at end of file diff --git a/src/pages/JobDescriptionPage.jsx b/src/pages/JobDescriptionPage.jsx new file mode 100644 index 0000000..f28fb28 --- /dev/null +++ b/src/pages/JobDescriptionPage.jsx @@ -0,0 +1,8 @@ +function JobDescriptionPage(params) { + return( +

+ JobDescriptionPage

+ ) +} + +export default JobDescriptionPage \ No newline at end of file diff --git a/src/pages/LoginPage.jsx b/src/pages/LoginPage.jsx new file mode 100644 index 0000000..e565ac8 --- /dev/null +++ b/src/pages/LoginPage.jsx @@ -0,0 +1,53 @@ +import React, { useState } from 'react'; +import { useDispatch } from 'react-redux'; +import { loginSuccess, setError } from '../features/auth/authSlice'; +import { Box, Button, TextField, Typography, Alert } from '@mui/material'; + +const LoginPage = () => { + const [email, setEmail] = useState(''); + const [password, setPassword] = useState(''); + const [error, setErrorState] = useState(null); + const dispatch = useDispatch(); + + const handleLogin = () => { + if (!email || !password) { + setErrorState('Email and Password are required.'); + return; + } + + // Simulate login API call + try { + dispatch(loginSuccess({ email })); // Replace with actual API response + } catch (err) { + dispatch(setError(err.message)); + setErrorState('Invalid credentials.'); + } + }; + + return ( + + Login + {error && {error}} + setEmail(e.target.value)} + /> + setPassword(e.target.value)} + /> + + + ); +}; + +export default LoginPage; \ No newline at end of file diff --git a/src/pages/ResourceInvitePage.jsx b/src/pages/ResourceInvitePage.jsx new file mode 100644 index 0000000..1a17144 --- /dev/null +++ b/src/pages/ResourceInvitePage.jsx @@ -0,0 +1,8 @@ +function ResourceInvitePage(params) { + return( +

+ ResourceInvitePage

+ ) +} + +export default ResourceInvitePage \ No newline at end of file diff --git a/src/theme.js b/src/theme.js new file mode 100644 index 0000000..6219be2 --- /dev/null +++ b/src/theme.js @@ -0,0 +1,49 @@ +import { createTheme } from '@mui/material/styles'; + +const lightTheme = createTheme({ + palette: { + mode: 'light', + primary: { + main: '#2196f3', // Light Blue + }, + secondary: { + main: '#90caf9', // Lighter Blue + }, + background: { + default: '#f5f5f5', // Light Gray + paper: '#ffffff', // White + }, + text: { + primary: '#000000', // Black + secondary: '#555555', // Dark Gray + }, + }, + typography: { + fontFamily: `'Roboto', 'Arial', sans-serif`, + }, +}); + +const darkTheme = createTheme({ + palette: { + mode: 'dark', + primary: { + main: '#2196f3', // Light Blue + }, + secondary: { + main: '#64b5f6', // Slightly Lighter Blue + }, + background: { + default: '#121212', // Black + paper: '#1e1e1e', // Dark Gray + }, + text: { + primary: '#ffffff', // White + secondary: '#bbbbbb', // Light Gray + }, + }, + typography: { + fontFamily: `'Roboto', 'Arial', sans-serif`, + }, +}); + +export { lightTheme, darkTheme };