diff --git a/Web/WebSocket/websocket-chat/apps/frontend-react/src/components/LoginForm/LoginForm.tsx b/Web/WebSocket/websocket-chat/apps/frontend-react/src/components/LoginForm/LoginForm.tsx index 9f2331b..ef6651e 100644 --- a/Web/WebSocket/websocket-chat/apps/frontend-react/src/components/LoginForm/LoginForm.tsx +++ b/Web/WebSocket/websocket-chat/apps/frontend-react/src/components/LoginForm/LoginForm.tsx @@ -1,39 +1,67 @@ import { useState } from 'react'; -import ILoginFormPops from './ILoginFormProps'; -import { Input } from './Styled'; +import type { ILoginFormProps } from './ILoginFormProps'; +import { FlexForm, Input, Button, FormTitle, FormSubtitle, InputContainer, ErrorMessage } from './Styled'; export function LoginForm({ onSubmit }: ILoginFormProps) { const [username, setUsername] = useState(''); const [secret, setSecret] = useState(''); + const [isLoading, setIsLoading] = useState(false); + const [error, setError] = useState(''); - // The submit handler now correctly uses 'event' - const handleFormSubmit = (event: React.FormEvent) => { + const handleFormSubmit = async (event: React.FormEvent) => { event.preventDefault(); - onSubmit(username, secret); + + if (!username.trim() || !secret.trim()) { + setError('Please fill in all fields'); + return; + } + + setIsLoading(true); + setError(''); + + try { + await onSubmit(username, secret); + } catch (err) { + setError('Login failed. Please try again.'); + } finally { + setIsLoading(false); + } }; return ( -
- {/* - - The 'placeholder' prop is now lowercase. - - The 'onChange' handler correctly destructures {target}. - */} - setUsername(target.value)} - /> - setSecret(target.value)} - /> - - -
+ + Welcome Back + Sign in to your account to continue + + + setUsername(target.value)} + disabled={isLoading} + required + /> + + + + setSecret(target.value)} + disabled={isLoading} + required + /> + + + {error && {error}} + + + ); } diff --git a/Web/WebSocket/websocket-chat/apps/frontend-react/src/components/LoginForm/Styled.tsx b/Web/WebSocket/websocket-chat/apps/frontend-react/src/components/LoginForm/Styled.tsx index 3536915..f26f08b 100644 --- a/Web/WebSocket/websocket-chat/apps/frontend-react/src/components/LoginForm/Styled.tsx +++ b/Web/WebSocket/websocket-chat/apps/frontend-react/src/components/LoginForm/Styled.tsx @@ -1,15 +1,154 @@ -import styled from 'styled-components'; +import styled, { keyframes } from 'styled-components'; + +const fadeIn = keyframes` + from { + opacity: 0; + transform: translateY(20px); + } + to { + opacity: 1; + transform: translateY(0); + } +`; + +const industrialGlow = keyframes` + 0% { + box-shadow: 0 0 0 0 rgba(255, 255, 255, 0.1); + } + 50% { + box-shadow: 0 0 0 8px rgba(255, 255, 255, 0.05); + } + 100% { + box-shadow: 0 0 0 0 rgba(255, 255, 255, 0.1); + } +`; + +export const FlexForm = styled.form` + display: flex; + flex-direction: column; + gap: 1.5rem; + padding: 3rem 3rem 3rem 3rem; + background: #2a2a2a; + border: 2px solid #111111; + border-radius: 0; + box-shadow: 0 8px 24px rgba(0, 0, 0, 0.6); + animation: ${fadeIn} 0.6s ease-out; + width: 450px; + box-sizing: border-box; + + &:focus { + outline: none; + } +`; export const Input = styled.input` width: 100%; - heght: 1.3rem; - backgroundColor: blue -` + padding: 1rem 1.25rem; + font-size: 0.9rem; + font-weight: 400; + font-family: 'Courier New', monospace; + background: #0a0a0a; + border: 1px solid #404040; + border-radius: 0; + color: #ffffff; + transition: all 0.2s ease; + text-transform: uppercase; + letter-spacing: 1px; + box-sizing: border-box; + + &::placeholder { + color: #666666; + font-weight: 400; + text-transform: uppercase; + letter-spacing: 1px; + } + + &:focus { + outline: none; + border-color: #ffffff; + background: #000000; + } + + &:hover { + border-color: #666666; + } +`; export const Button = styled.button` - width: fit-content; - margin: auto; - text-align: center; - background-color: #a7c42; + width: 100%; + padding: 1.1rem 1.5rem; + font-size: 0.95rem; + font-weight: 600; + font-family: 'Courier New', monospace; + background: #000000; + color: #ffffff; + border: 2px solid #ffffff; + border-radius: 0; + cursor: pointer; + transition: all 0.2s ease; + text-transform: uppercase; + letter-spacing: 2px; + margin-top: 0.5rem; + box-sizing: border-box; + + &:hover { + background: #ffffff; + color: #000000; + } + + &:active { + background: #333333; + color: #ffffff; + border-color: #cccccc; + } + + &:disabled { + opacity: 0.4; + cursor: not-allowed; + border-color: #666666; + color: #666666; + } +`; -` +export const FormTitle = styled.h1` + font-size: 1.4rem; + font-weight: 700; + font-family: 'Courier New', monospace; + color: #ffffff; + text-align: center; + margin: 0 0 0.5rem 0; + text-transform: uppercase; + letter-spacing: 3px; +`; + +export const FormSubtitle = styled.p` + font-size: 0.75rem; + font-family: 'Courier New', monospace; + color: #999999; + text-align: center; + margin: 0 0 1.5rem 0; + font-weight: 400; + line-height: 1.5; + text-transform: uppercase; + letter-spacing: 1px; +`; + +export const InputContainer = styled.div` + width: 100%; +`; + +export const ErrorMessage = styled.div` + color: #ffffff; + font-size: 0.8rem; + font-weight: 400; + font-family: 'Courier New', monospace; + text-align: center; + padding: 0.75rem 1rem; + background: #000000; + border: 1px solid #333333; + border-radius: 0; + margin: 0.5rem 0; + text-transform: uppercase; + letter-spacing: 1px; + animation: ${fadeIn} 0.3s ease-out; +`; diff --git a/Web/WebSocket/websocket-chat/apps/frontend-react/src/index.css b/Web/WebSocket/websocket-chat/apps/frontend-react/src/index.css index 08a3ac9..9364aae 100644 --- a/Web/WebSocket/websocket-chat/apps/frontend-react/src/index.css +++ b/Web/WebSocket/websocket-chat/apps/frontend-react/src/index.css @@ -1,11 +1,13 @@ +@import url('https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@300;400;500;600;700;800;900&display=swap'); + :root { - font-family: system-ui, Avenir, Helvetica, Arial, sans-serif; - line-height: 1.5; + font-family: 'JetBrains Mono', 'Courier New', monospace; + line-height: 1.4; font-weight: 400; - color-scheme: light dark; - color: rgba(255, 255, 255, 0.87); - background-color: #242424; + color-scheme: dark; + color: #ffffff; + background-color: #000000; font-synthesis: none; text-rendering: optimizeLegibility; @@ -13,56 +15,142 @@ -moz-osx-font-smoothing: grayscale; } -a { - font-weight: 500; - color: #646cff; - text-decoration: inherit; +* { + box-sizing: border-box; + margin: 0; + padding: 0; } -a:hover { - color: #535bf2; + +html { + margin: 0; + padding: 0; + width: 100%; + height: 100%; + overflow-x: hidden; + background-color: #000000; } body { margin: 0; - display: flex; - place-items: center; - min-width: 320px; - min-height: 100vh; + padding: 0; + width: 100%; + height: 100%; + overflow-x: hidden; + background: + radial-gradient(circle at 25% 25%, rgba(255, 255, 255, 0.01) 0%, transparent 50%), + radial-gradient(circle at 75% 75%, rgba(255, 255, 255, 0.01) 0%, transparent 50%), + linear-gradient(135deg, #000000 0%, #0a0a0a 50%, #000000 100%); + background-attachment: fixed; +} + +#root { + margin: 0; + padding: 0; + width: 100%; + height: 100vh; + overflow-x: hidden; +} + +a { + font-weight: 600; + color: #ffffff; + text-decoration: none; + text-transform: uppercase; + letter-spacing: 1px; + transition: all 0.2s ease; +} + +a:hover { + color: #cccccc; + text-shadow: 0 0 5px rgba(255, 255, 255, 0.3); +} + +h1, h2, h3, h4, h5, h6 { + font-family: 'JetBrains Mono', 'Courier New', monospace; + font-weight: 700; + color: #ffffff; + margin: 0; + text-transform: uppercase; + letter-spacing: 1px; } h1 { - font-size: 3.2em; + font-size: 2.5rem; line-height: 1.1; + font-weight: 900; + letter-spacing: 3px; +} + +p { + font-family: 'JetBrains Mono', 'Courier New', monospace; + color: #cccccc; + line-height: 1.5; } 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; + font-family: 'JetBrains Mono', 'Courier New', monospace; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 1px; } -@media (prefers-color-scheme: light) { - :root { - color: #213547; - background-color: #ffffff; - } - a:hover { - color: #747bff; - } - button { - background-color: #f9f9f9; - } +input, textarea { + font-family: 'JetBrains Mono', 'Courier New', monospace; +} + +/* Scrollbar styling for post-industrial feel */ +::-webkit-scrollbar { + width: 8px; +} + +::-webkit-scrollbar-track { + background: #000000; + border: 1px solid #333333; +} + +::-webkit-scrollbar-thumb { + background: #666666; + border: 1px solid #000000; +} + +::-webkit-scrollbar-thumb:hover { + background: #888888; +} + +/* Selection styling */ +::selection { + background: rgba(255, 255, 255, 0.2); + color: #ffffff; +} + +::-moz-selection { + background: rgba(255, 255, 255, 0.2); + color: #ffffff; +} + +/* Focus styling */ +:focus-visible { + outline: 2px solid #ffffff; + outline-offset: 2px; +} + +/* Utility classes */ +.text-center { + text-align: center; +} + +.text-uppercase { + text-transform: uppercase; +} + +.font-mono { + font-family: 'JetBrains Mono', 'Courier New', monospace; +} + +.font-weight-bold { + font-weight: 700; +} + +.letter-spacing { + letter-spacing: 1px; } diff --git a/Web/WebSocket/websocket-chat/apps/frontend-react/src/views/LoginView/index.tsx b/Web/WebSocket/websocket-chat/apps/frontend-react/src/views/LoginView/index.tsx index 9ab1044..7b5e9b0 100644 --- a/Web/WebSocket/websocket-chat/apps/frontend-react/src/views/LoginView/index.tsx +++ b/Web/WebSocket/websocket-chat/apps/frontend-react/src/views/LoginView/index.tsx @@ -1,14 +1,39 @@ +import styled from 'styled-components'; import {LoginForm} from '../../components'; +const FlexContainer = styled.div` + width: 100%; + height: 100vh; + margin: 0; + padding: 0; + display: flex; + align-items: center; + justify-content: center; + background: #000000; + background-image: + linear-gradient(90deg, rgba(255, 255, 255, 0.02) 1px, transparent 1px), + linear-gradient(180deg, rgba(255, 255, 255, 0.02) 1px, transparent 1px); + background-size: 50px 50px; + overflow: hidden; +`; + +const LoginFormContainer = styled.div` + display: flex; + align-items: center; + justify-content: center; + margin: 0; + padding: 0; +`; + export function LoginView() { const handleLoginSubmit = (username: string, secret: string) => { console.log('handling login submit username [%o] secret [%o]', username, secret) }; - console.log('LoginView...') - - return <> - - + return + + + + }