DEV Community

Nebula
Nebula

Posted on

nebula

import React from 'react';
import styled from 'styled-components';
import { motion } from 'framer-motion';
import { useNavigate } from 'react-router-dom';
import { useAuth } from '@/contexts/AuthContext';
import { useForm } from 'react-hook-form';
import toast from 'react-hot-toast';
import { InputGroup } from './Dashboard/CreateAccountStyles';
import { FaSpinner } from 'react-icons/fa';

// Design tokens
const colors = {
  primary: '#667eea',
  primaryDark: '#5a67d8',
  secondary: '#764ba2',
  success: '#48bb78',
  error: '#ff6b6b',
  warning: '#f6ad55',
  text: '#1a202c',
  textSecondary: '#4a5568',
  textMuted: '#a0aec0',
  background: 'rgba(255, 255, 255, 0.95)',
  border: 'rgba(102, 126, 234, 0.2)',
  borderHover: '#667eea',
  glass: 'rgba(255, 255, 255, 0.95)',
  glassLight: 'rgba(255, 255, 255, 0.8)',
  shadow: 'rgba(0, 0, 0, 0.1)',
  shadowHover: 'rgba(102, 126, 234, 0.1)'
};

const gradients = {
  primary: `linear-gradient(135deg, ${colors.primary}, ${colors.secondary})`,
  success: `linear-gradient(135deg, ${colors.success}, #38b2ac)`,
  error: `linear-gradient(135deg, ${colors.error}, #ee5a52)`,
  warning: `linear-gradient(135deg, ${colors.warning}, #ff8c00)`
};

const spacing = {
  xs: '4px',
  sm: '8px',
  md: '12px',
  lg: '16px',
  xl: '20px',
  xxl: '24px',
  xxxl: '32px'
};

// Styled Components
const Container = styled.div`
  min-height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: ${spacing.xl};
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  position: relative;

  &::before {
    content: '';
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    background: 
      radial-gradient(circle at 20% 80%, rgba(120, 119, 198, 0.3) 0%, transparent 50%),
      radial-gradient(circle at 80% 20%, rgba(255, 255, 255, 0.1) 0%, transparent 50%),
      radial-gradient(circle at 40% 40%, rgba(120, 119, 198, 0.2) 0%, transparent 50%);
  }
`;

const FormWrapper = styled(motion.div)`
  background: ${colors.glass};
  backdrop-filter: blur(20px);
  border-radius: 20px;
  padding: ${spacing.xxxl};
  box-shadow: 0 8px 32px ${colors.shadow};
  border: 1px solid rgba(255, 255, 255, 0.2);
  position: relative;
  overflow: hidden;
  width: 100%;
  max-width: 420px;
  z-index: 1;

  &::before {
    content: '';
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    height: 4px;
    background: ${gradients.primary};
  }

  @media (max-width: 768px) {
    padding: ${spacing.xxl};
    max-width: 100%;
  }
`;

const Title = styled.h1`
  font-size: 32px;
  font-weight: 800;
  color: ${colors.text};
  margin-bottom: ${spacing.sm};
  background: ${gradients.primary};
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
  background-clip: text;
  text-align: center;
  line-height: 1.2;

  @media (max-width: 768px) {
    font-size: 28px;
  }
`;

const Subtitle = styled.p`
  font-size: 16px;
  color: ${colors.textSecondary};
  text-align: center;
  margin-bottom: ${spacing.xxxl};
  font-weight: 500;
  line-height: 1.4;
`;

const Form = styled.form`
  display: flex;
  flex-direction: column;
  gap: ${spacing.xl};
`;

const StyledInputGroup = styled.div`
  display: flex;
  flex-direction: column;
  gap: ${spacing.xs};
`;

const Input = styled.input`
  width: 100%;
  padding: ${spacing.lg} ${spacing.xl};
  border: 2px solid ${props => props.errors ? colors.error : colors.border};
  border-radius: 12px;
  font-size: 16px;
  font-weight: 500;
  color: ${colors.text};
  background: ${colors.glassLight};
  backdrop-filter: blur(10px);
  transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
  box-sizing: border-box;

  &:focus {
    outline: none;
    border-color: ${props => props.errors ? colors.error : colors.borderHover};
    box-shadow: 0 0 0 4px ${props => props.errors ? 'rgba(255, 107, 107, 0.1)' : colors.shadowHover};
    background: ${colors.background};
  }

  &::placeholder {
    color: ${colors.textMuted};
  }

  &:-webkit-autofill {
    -webkit-box-shadow: 0 0 0 30px ${colors.background} inset;
    -webkit-text-fill-color: ${colors.text};
  }
`;

const ErrorMessage = styled.p`
  color: ${colors.error};
  font-size: 14px;
  margin: 0;
  font-weight: 500;
  margin-top: ${spacing.xs};
`;

const Button = styled(motion.button)`
  width: 100%;
  padding: ${spacing.lg} ${spacing.xl};
  border-radius: 12px;
  font-size: 16px;
  font-weight: 600;
  cursor: pointer;
  border: none;
  transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
  background: ${gradients.primary};
  color: white;
  box-shadow: 0 4px 16px rgba(102, 126, 234, 0.4);
  display: flex;
  align-items: center;
  justify-content: center;
  gap: ${spacing.sm};
  margin-top: ${spacing.md};

  &:hover:not(:disabled) {
    transform: translateY(-2px);
    box-shadow: 0 8px 25px rgba(102, 126, 234, 0.5);
  }

  &:active {
    transform: translateY(0);
  }

  &:disabled {
    opacity: 0.6;
    cursor: not-allowed;
  }

  svg {
    animation: spin 1s linear infinite;
  }

  @keyframes spin {
    from { transform: rotate(0deg); }
    to { transform: rotate(360deg); }
  }
`;

const SignupText = styled.p`
  text-align: center;
  margin-top: ${spacing.xxxl};
  color: ${colors.textSecondary};
  font-size: 14px;
  font-weight: 500;
`;

const SignupLink = styled.a`
  color: ${colors.primary};
  text-decoration: none;
  font-weight: 600;
  margin-left: ${spacing.xs};
  transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);

  &:hover {
    color: ${colors.primaryDark};
    text-decoration: underline;
  }
`;

function Login() {
  const navigate = useNavigate();
  const { login } = useAuth();
  const { register, handleSubmit, formState: { errors, isSubmitting } } = useForm();

  const onSubmit = async (data) => {
    try {
      const userData = await login({
        email: data.email,
        password: data.password
      });
      toast.success("Logged in successfully!");
      setTimeout(() => {
        navigate("/dashboard/profile");
      }, 1000);
    } catch (error) {
      console.error("Login Error ", error);
      toast.error(error.response?.data?.message || error.message || "Login Failed. Please try again!");
    }
  };

  return (
    <Container>
      <FormWrapper
        initial={{ opacity: 0, y: 20 }}
        animate={{ opacity: 1, y: 0 }}
        transition={{ duration: 0.5, ease: 'easeOut' }}
        aria-label="Login form"
      >
        <Title>Banking Application</Title>
        <Subtitle>Secure Login to Your Account</Subtitle>
        <Form onSubmit={handleSubmit(onSubmit)}>
          <StyledInputGroup>
            <Input
              id="email"
              type="text"
              placeholder="Email"
              errors={errors.email}
              {...register('email')}
            />
            {errors.email && (
              <ErrorMessage>
                {errors.email.message}
              </ErrorMessage>
            )}
          </StyledInputGroup>

          <StyledInputGroup>
            <Input
              id="password"
              type="password"
              placeholder="Password"
              errors={errors.password}
              {...register('password')}
            />
            {errors.password && (
              <ErrorMessage>
                {errors.password.message}
              </ErrorMessage>
            )}
          </StyledInputGroup>

          <Button
            whileHover={{ scale: 1.02 }}
            whileTap={{ scale: 0.98 }}
            type="submit"
            aria-label="Log In"
            disabled={isSubmitting}
          >
            {isSubmitting ? <FaSpinner /> : "Login"}
          </Button>
        </Form>

        <SignupText>
          New here?
          <SignupLink href="/signup"> Sign up</SignupLink>
        </SignupText>
      </FormWrapper>
    </Container>
  );
}

export default Login;
Enter fullscreen mode Exit fullscreen mode

Top comments (0)