diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 57c78b3..fee3b1d 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -1,13 +1,13 @@ import './App.css' import Greeting from './components/Greeting' import Hosting from './components/Hosting' -import NameSelector from './components/NameSelector' +import InitialSetup from './components/InitialSetup' import Program from './components/Program' function App() { return ( <> - +
diff --git a/frontend/src/components/Attendance.tsx b/frontend/src/components/Attendance.tsx new file mode 100644 index 0000000..e69de29 diff --git a/frontend/src/components/InitialSetup.tsx b/frontend/src/components/InitialSetup.tsx new file mode 100644 index 0000000..da6eebf --- /dev/null +++ b/frontend/src/components/InitialSetup.tsx @@ -0,0 +1,152 @@ +import { useState, useEffect } from 'react'; +import { useCookies } from 'react-cookie'; +import { GUESTS } from '../constants/constants'; +import useFetchUser from '../utils/fetchUser'; // Import your custom hook +import { useNotification } from '../NotificationContext'; + +const InitialSetup = () => { + const [cookie, setCookie] = useCookies(); + const [selectedName, setSelectedName] = useState(cookie.userName); + const [token, setToken] = useState(cookie.token) + const [isSubmitted, setIsSubmitted] = useState(false); + const [password, setPassword] = useState(''); + const [isPasswordSet, setIsPasswordSet] = useState(false); // To track if password is set + const [isSignIn, setIsSignIn] = useState(false); // To track if it should prompt for sign-in + + const { userSet, passwordCreate, signUser, validToken } = useFetchUser(); // Destructure functions from the hook + const notify = useNotification(); + + useEffect(() => { + document.body.style.overflow = 'hidden'; + + return () => { + document.body.style.overflow = 'unset'; + }; + }, []); + + const handleSelect = (event: React.ChangeEvent) => { + const name = event.target.value; + setSelectedName(name); + checkUserPassword(name); + }; + + const checkUserPassword = async (name: string) => { + const passwordStatus = await userSet(name); + setIsPasswordSet(passwordStatus); + setIsSignIn(!passwordStatus); // If password is not set, show sign-up + }; + + const handlePasswordCreate = async () => { + const message= await passwordCreate(selectedName!, password); + if (message !== '') { + notify(message, 'error') + return + } + setIsSubmitted(true); + }; + + const handleSignIn = async () => { + const signedIn = await signUser(selectedName!, password); // Implement your sign-in logic here + if (!signedIn) { + notify('Не удалось войти. Может пароль не тот?', 'error') + return + } + setIsSubmitted(true); + }; + + if (isSubmitted) { + console.log('Selected', selectedName); + return null; // or you can redirect to another component or page + } + + useEffect(() => { + const validateToken = async () => { + const isTokenValid = await validToken(token); + setIsSubmitted(isTokenValid); + }; + + validateToken(); + }, [token]); + + return ( +
+

Выбери себя

+ + {(selectedName !== undefined) && ( + <> +

{isPasswordSet ? 'Войдите' : 'Создайте свой пароль'}

+ setPassword(e.target.value)} + style={styles.input} + /> + {isPasswordSet ? ( + <> + + + ) : (<> + + )} + + + )} +
+ ); +}; + +const styles = { + container: { + position: 'fixed' as 'fixed', + top: 0, + left: 0, + width: '100vw', + height: '100vh', + backgroundColor: 'rgba(0, 0, 0, 1)', + display: 'flex', + flexDirection: 'column' as 'column', + justifyContent: 'center', + alignItems: 'center', + color: '#fff', + zIndex: 1000, + overflow: 'hidden', + }, + title: { + marginBottom: '20px', + }, + dropdown: { + padding: '10px', + fontSize: '16px', + cursor: 'pointer', + border: 'none', + borderRadius: '5px', + outline: 'none', + marginBottom: '10px', + }, + input: { + padding: '10px', + fontSize: '16px', + border: 'none', + borderRadius: '5px', + outline: 'none', + marginBottom: '10px', + width: '200px', // Set a width + } +} + +export default InitialSetup; \ No newline at end of file diff --git a/frontend/src/components/NameSelector.tsx b/frontend/src/components/NameSelector.tsx deleted file mode 100644 index cc16807..0000000 --- a/frontend/src/components/NameSelector.tsx +++ /dev/null @@ -1,64 +0,0 @@ -import { useState } from 'react'; -import { useCookies } from 'react-cookie'; -import { GUESTS } from '../constants/constants'; - -const NameSelector = () => { - const [cookie, setCookie] = useCookies(['userName']); - const [selectedName, setSelectedName] = useState(cookie.userName); - - const handleSelect = (name: string) => { - if (name) - setSelectedName(name); - setCookie('userName', name, { path: '/' }); - }; - - if (selectedName !== undefined) { - console.log('Selected', selectedName) - return null; - } - - return ( -
-

Выбери себя

-
- {GUESTS.map((name) => ( - - ))} -
-
- ); -}; - -const styles = { - container: { - position: 'fixed' as 'fixed', - top: 0, - left: 0, - width: '100vw', - height: '100vh', - backgroundColor: 'rgba(0, 0, 0, 1)', - display: 'flex', - flexDirection: 'column' as 'column', - justifyContent: 'center', - alignItems: 'center', - color: '#fff', - zIndex: 1000, - }, - title: { - marginBottom: '20px', - }, - namesContainer: { - display: 'flex', - flexDirection: 'column' as 'column', - }, - button: { - margin: '10px', - padding: '10px 20px', - fontSize: '16px', - cursor: 'pointer', - }, -}; - -export default NameSelector; diff --git a/frontend/src/utils/fetchUser.tsx b/frontend/src/utils/fetchUser.tsx new file mode 100644 index 0000000..0cdafe3 --- /dev/null +++ b/frontend/src/utils/fetchUser.tsx @@ -0,0 +1,124 @@ +import { useState } from 'react'; +import { useCookies } from 'react-cookie'; + +interface User { + password?: string; +} + +interface UserData { + [key: string]: User; +} + +const useFetchUser = () => { + const [cookies, setCookie] = useCookies(['apiToken']); + + const userSet = async (userName: string): Promise => { + try { + const response = await fetch(`/users/isSet?userName=${encodeURIComponent(userName)}`); + if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`); + const data = await response.json(); + return data; // Assuming the server returns true/false + } catch (error) { + console.error('Error checking user password status:', error); + return false; + } + }; + + const passwordCreate = async (userName: string, password: string): Promise => { + // Simple validation: password should not be empty and should have a minimum length + if (!password || password.length < 6) { + console.error('Password should be at least 6 characters long.'); + return 'Password should be at least 6 characters long.'; + } + + try { + const hashedPassword = await hashPassword(password); // Implement this function to hash the password + const response = await fetch(`/users/createPassword`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + userName, + password: hashedPassword, + }), + }); + + if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`); + const data = await response.json(); + + if (data.success) { + setCookie('apiToken', data.token, { path: '/' }); + console.log(`Password created for ${userName}`); + return ''; // Password creation success + } + return 'Не удалось создать пароль'; // Password creation failure + } catch (error) { + console.error('Error creating password:', error); + return 'Пароль не создан:' + error; + } + }; + + const signUser = async (userName: string, password: string): Promise => { + try { + const hashedPassword = await hashPassword(password); // Implement this function to hash the password + const response = await fetch(`/login`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + userName, + password: hashedPassword, + }), + }); + + if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`); + const data = await response.json(); + + if (data.token) { + setCookie('apiToken', data.token, { path: '/' }); + console.log(`User ${userName} signed in.`); + return true; // Sign-in success + } + return false; // Sign-in failed + } catch (error) { + console.error('Error logging in:', error); + return false; + } + }; + + const validToken = async (token: string | undefined): Promise => { + try { + const response = await fetch(`/login/validateToken`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + token, + }), + }); + + if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`); + const data = await response.json(); + + if (data.tokenValid) { + return true; + } + return false; + } catch (error) { + console.error('Error validating token:', error); + return false; + } + } + + const hashPassword = async (password: string): Promise => { + // Placeholder implementation, replace with actual hashing logic (e.g. bcrypt) + return Promise.resolve(password); // Just return the password for now + }; + + return { userSet, passwordCreate, signUser, validToken }; +}; + +export default useFetchUser;