DEV Community

Nebula
Nebula

Posted on

FRONTEND

import styled from 'styled-components';
import { motion } from 'framer-motion';

const DashboardContainer = styled.div`
  display: flex;
  min-height: 100vh;
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
`;

const Sidebar = styled(motion.aside)`
  width: 280px;
  background: rgba(255, 255, 255, 0.95);
  backdrop-filter: blur(20px);
  border-right: 1px solid rgba(255, 255, 255, 0.2);
  padding: 32px 0;
  display: flex;
  flex-direction: column;
  box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
  position: relative;
  z-index: 10;

  h2 {
    font-size: 24px;
    font-weight: 700;
    color: #1a202c;
    margin: 0 32px 48px;
    background: linear-gradient(135deg, #667eea, #764ba2);
    -webkit-background-clip: text;
    -webkit-text-fill-color: transparent;
    background-clip: text;
  }

  @media (max-width: 768px) {
    width: 240px;
  }
`;

const SidebarItem = styled(motion.div)`
  padding: 16px 32px;
  margin: 4px 16px;
  border-radius: 12px;
  color: #4a5568;
  font-weight: 500;
  cursor: pointer;
  transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
  position: relative;
  text-transform: capitalize;

  &:hover {
    background: linear-gradient(135deg, #667eea, #764ba2);
    color: white;
    transform: translateX(8px);
    box-shadow: 0 4px 20px rgba(102, 126, 234, 0.4);
  }

  &:active {
    transform: translateX(4px) scale(0.98);
  }
`;

const LogoutButton = styled(motion.button)`
  margin: auto 16px 16px;
  padding: 16px 32px;
  background: linear-gradient(135deg, #ff6b6b, #ee5a52);
  color: white;
  border: none;
  border-radius: 12px;
  font-weight: 600;
  cursor: pointer;
  transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
  box-shadow: 0 4px 16px rgba(238, 90, 82, 0.3);

  &:hover {
    transform: translateY(-2px);
    box-shadow: 0 8px 25px rgba(238, 90, 82, 0.4);
    background: linear-gradient(135deg, #ff5252, #e53e3e);
  }

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

const MainContent = styled.div`
  flex: 1;
  display: flex;
  flex-direction: column;
  background: rgba(255, 255, 255, 0.1);
  backdrop-filter: blur(10px);
  border-radius: 24px 0 0 24px;
  margin: 16px 16px 16px 0;
  overflow: hidden;
`;

const Header = styled(motion.header)`
  background: rgba(255, 255, 255, 0.95);
  backdrop-filter: blur(20px);
  padding: 24px 32px;
  display: flex;
  justify-content: space-between;
  align-items: center;
  border-bottom: 1px solid rgba(255, 255, 255, 0.2);
  box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08);
`;

const UserInfo = styled(motion.div)`
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 12px 20px;
  background: linear-gradient(135deg, rgba(102, 126, 234, 0.1), rgba(118, 75, 162, 0.1));
  border-radius: 50px;
  border: 1px solid rgba(102, 126, 234, 0.2);

  svg {
    color: #667eea;
    font-size: 18px;
  }
`;

const UserName = styled.span`
  font-weight: 600;
  color: #2d3748;
  text-transform: capitalize;
`;

const HeaderButton = styled(motion.button)`
  background: linear-gradient(135deg, rgba(255, 107, 107, 0.1), rgba(238, 90, 82, 0.1));
  border: 1px solid rgba(255, 107, 107, 0.2);
  border-radius: 12px;
  padding: 12px;
  cursor: pointer;
  transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
  display: flex;
  align-items: center;
  justify-content: center;

  svg {
    color: #ff6b6b;
    font-size: 18px;
  }

  &:hover {
    background: linear-gradient(135deg, #ff6b6b, #ee5a52);
    transform: translateY(-2px);
    box-shadow: 0 8px 25px rgba(255, 107, 107, 0.3);

    svg {
      color: white;
    }
  }

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

// Animation variants for framer-motion
const sidebarVariants = {
  initial: { x: -280, opacity: 0 },
  animate: { x: 0, opacity: 1, transition: { duration: 0.6, ease: "easeOut" } }
};

const headerVariants = {
  initial: { y: -60, opacity: 0 },
  animate: { y: 0, opacity: 1, transition: { duration: 0.6, delay: 0.2, ease: "easeOut" } }
};

const sidebarItemVariants = {
  initial: { x: -20, opacity: 0 },
  animate: { x: 0, opacity: 1 },
  hover: { x: 8, transition: { duration: 0.2 } }
};

const userInfoVariants = {
  initial: { scale: 0.9, opacity: 0 },
  animate: { scale: 1, opacity: 1, transition: { duration: 0.4, delay: 0.4 } }
};

// Add these props to your components:
// <Sidebar variants={sidebarVariants} initial="initial" animate="animate">
// <Header variants={headerVariants} initial="initial" animate="animate">
// <SidebarItem variants={sidebarItemVariants} whileHover="hover">
// <UserInfo variants={userInfoVariants} initial="initial" animate="animate">

import { FaSignOutAlt, FaUser } from "react-icons/fa";
import { Outlet } from "react-router-dom";

function Dashboard() {
  return (
    <DashboardContainer>
      <Sidebar>
        <h2>Banking App</h2>
        <SidebarItem>Profile</SidebarItem>
        <SidebarItem>Create account</SidebarItem>
        <SidebarItem>view transactions</SidebarItem>
        <SidebarItem>deposit</SidebarItem>
        <SidebarItem>withdraw</SidebarItem>
        <SidebarItem>take loan</SidebarItem>
        <SidebarItem>transfer money</SidebarItem>
        <SidebarItem>View analytics</SidebarItem>
        <LogoutButton>Logout</LogoutButton>
      </Sidebar>
      <MainContent>
        <Header>
          <UserInfo>
            <FaUser />
            <UserName>username</UserName>
          </UserInfo>
          <HeaderButton>
            <FaSignOutAlt />
          </HeaderButton>
        </Header>
        <Outlet />
      </MainContent>
    </DashboardContainer>
  );
}

export default Dashboard;
Enter fullscreen mode Exit fullscreen mode

---------------- PROFILE PAGE ---------------

import styled from 'styled-components';
import { motion } from 'framer-motion';

const ProfileContainer = styled(motion.div)`
  padding: 32px;
  max-width: 1200px;
  margin: 0 auto;
  min-height: calc(100vh - 120px);

  h3 {
    font-size: 24px;
    font-weight: 700;
    color: #1a202c;
    margin: 48px 0 24px;
    background: linear-gradient(135deg, #667eea, #764ba2);
    -webkit-background-clip: text;
    -webkit-text-fill-color: transparent;
    background-clip: text;
  }

  @media (max-width: 768px) {
    padding: 20px;
  }
`;

const ProfileTitle = styled(motion.h2)`
  font-size: 32px;
  font-weight: 800;
  color: #1a202c;
  margin-bottom: 32px;
  background: linear-gradient(135deg, #667eea, #764ba2);
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
  background-clip: text;
  text-align: center;

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

const UserDetails = styled(motion.div)`
  background: rgba(255, 255, 255, 0.95);
  backdrop-filter: blur(20px);
  border-radius: 20px;
  padding: 32px;
  margin-bottom: 32px;
  box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
  border: 1px solid rgba(255, 255, 255, 0.2);
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
  gap: 24px;
  position: relative;
  overflow: hidden;

  &::before {
    content: '';
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    height: 4px;
    background: linear-gradient(135deg, #667eea, #764ba2);
  }

  @media (max-width: 768px) {
    padding: 24px;
    grid-template-columns: 1fr;
    gap: 16px;
  }
`;

const UserDetail = styled(motion.div)`
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 20px;
  background: linear-gradient(135deg, rgba(102, 126, 234, 0.05), rgba(118, 75, 162, 0.05));
  border-radius: 12px;
  border: 1px solid rgba(102, 126, 234, 0.1);
  transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);

  &:hover {
    transform: translateY(-4px);
    box-shadow: 0 8px 25px rgba(102, 126, 234, 0.15);
    border-color: rgba(102, 126, 234, 0.3);
  }

  strong {
    font-weight: 600;
    color: #4a5568;
    font-size: 14px;
    text-transform: uppercase;
    letter-spacing: 0.5px;
  }

  &::after {
    content: attr(data-value);
    font-size: 18px;
    font-weight: 700;
    color: #1a202c;
    margin-top: 4px;
  }
`;

const Table = styled(motion.table)`
  width: 100%;
  background: rgba(255, 255, 255, 0.95);
  backdrop-filter: blur(20px);
  border-radius: 20px;
  overflow: hidden;
  box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
  border: 1px solid rgba(255, 255, 255, 0.2);
  border-collapse: separate;
  border-spacing: 0;

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

const TableHeader = styled.th`
  background: linear-gradient(135deg, #667eea, #764ba2);
  color: white;
  padding: 20px 16px;
  text-align: left;
  font-weight: 600;
  font-size: 14px;
  text-transform: uppercase;
  letter-spacing: 0.5px;
  border: none;

  &:first-child {
    border-top-left-radius: 20px;
  }

  &:last-child {
    border-top-right-radius: 20px;
  }

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

// Fixed typo - this should match the component name
const TabeHeader = styled(TableHeader)``;

const TableRow = styled(motion.tr)`
  transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);

  &:hover {
    background: linear-gradient(135deg, rgba(102, 126, 234, 0.05), rgba(118, 75, 162, 0.05));
    transform: scale(1.01);
  }

  &:nth-child(even) {
    background: rgba(248, 250, 252, 0.5);
  }

  &:last-child td:first-child {
    border-bottom-left-radius: 20px;
  }

  &:last-child td:last-child {
    border-bottom-right-radius: 20px;
  }
`;

const TableCell = styled.td`
  padding: 20px 16px;
  border-bottom: 1px solid rgba(226, 232, 240, 0.5);
  color: #4a5568;
  font-weight: 500;
  border: none;
  vertical-align: middle;

  &:last-child {
    border-bottom: none;
  }

  @media (max-width: 768px) {
    padding: 16px 12px;
    font-size: 13px;
  }
`;

const ActionLink = styled(motion.button)`
  display: inline-flex;
  align-items: center;
  gap: 8px;
  background: linear-gradient(135deg, rgba(102, 126, 234, 0.1), rgba(118, 75, 162, 0.1));
  color: #667eea;
  border: 1px solid rgba(102, 126, 234, 0.2);
  padding: 10px 16px;
  border-radius: 25px;
  font-size: 13px;
  font-weight: 600;
  cursor: pointer;
  transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
  text-decoration: none;
  margin: 2px;

  svg {
    font-size: 12px;
  }

  &:hover {
    background: linear-gradient(135deg, #667eea, #764ba2);
    color: white;
    transform: translateY(-2px);
    box-shadow: 0 6px 20px rgba(102, 126, 234, 0.4);
    border-color: transparent;
  }

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

  @media (max-width: 768px) {
    padding: 8px 12px;
    font-size: 12px;
    gap: 6px;
  }
`;

// Animation variants for framer-motion
const containerVariants = {
  initial: { opacity: 0, y: 20 },
  animate: { 
    opacity: 1, 
    y: 0, 
    transition: { 
      duration: 0.6, 
      ease: "easeOut",
      staggerChildren: 0.1
    }
  }
};

const titleVariants = {
  initial: { opacity: 0, y: -20 },
  animate: { 
    opacity: 1, 
    y: 0, 
    transition: { duration: 0.6, ease: "easeOut" }
  }
};

const userDetailsVariants = {
  initial: { opacity: 0, scale: 0.95 },
  animate: { 
    opacity: 1, 
    scale: 1,
    transition: { 
      duration: 0.5, 
      ease: "easeOut",
      staggerChildren: 0.1
    }
  }
};

const userDetailVariants = {
  initial: { opacity: 0, x: -20 },
  animate: { opacity: 1, x: 0 },
  hover: { 
    y: -4,
    transition: { duration: 0.2 }
  }
};

const tableVariants = {
  initial: { opacity: 0, y: 30 },
  animate: { 
    opacity: 1, 
    y: 0,
    transition: { 
      duration: 0.6, 
      ease: "easeOut",
      staggerChildren: 0.05
    }
  }
};

const rowVariants = {
  initial: { opacity: 0, x: -20 },
  animate: { opacity: 1, x: 0 },
  hover: { 
    scale: 1.01,
    transition: { duration: 0.2 }
  }
};

const actionLinkVariants = {
  hover: { 
    y: -2, 
    transition: { duration: 0.2 }
  },
  tap: { 
    y: 0, 
    scale: 0.95 
  }
};

// Add these props to your components:
// <ProfileContainer variants={containerVariants}>
// <ProfileTitle variants={titleVariants}>
// <UserDetails variants={userDetailsVariants}>
// <UserDetail variants={userDetailVariants} whileHover="hover">
// <Table variants={tableVariants}>
// <TableRow variants={rowVariants} whileHover="hover">
// <ActionLink variants={actionLinkVariants} whileHover="hover" whileTap="tap">

import { FaEye } from "react-icons/fa";

function Profile() {
  const user = {
    name: "John Doe",
    email: "john.doe@example.com",
    age: 30,
  };

  const accounts = [
    {
      srNo: 1,
      accountNumber: "1234567890",
      holderName: "Kishan",
      balance: 1000.0,
      creationDate: "2023-01-01",
    },
    {
      srNo: 2,
      accountNumber: "0987654321",
      holderName: "Kishan",
      balance: 2000.0,
      creationDate: "2023-02-01",
    },
  ];
  return (
    <ProfileContainer
      initial={{ opacity: 0, y: 20 }}
      animate={{ opacity: 1, y: 0 }}
      transition={{ duration: 0.5, ease: "easeOut" }}
    >
      <ProfileTitle>User profile</ProfileTitle>
      <UserDetails>
        <UserDetail>
          <strong>Name:</strong> {user.name}
        </UserDetail>
        <UserDetail>
          <strong>Email:</strong> {user.email}
        </UserDetail>
        <UserDetail>
          <strong>Age:</strong> {user.age}
        </UserDetail>
      </UserDetails>
      <h3>Accounts</h3>
      <Table>
        <thead>
          <tr>
            <TabeHeader>Sr. No</TabeHeader>
            <TabeHeader>Account Number</TabeHeader>
            <TableHeader>ACcount holder name</TableHeader>
            <TableHeader>Date of creation</TableHeader>
            <TableHeader>Account Balance</TableHeader>
            <TableHeader>Actions</TableHeader>
          </tr>
        </thead>

        <tbody>
          {accounts.map((account) => (
            <TableRow key={account.srNo}>
              <TableCell>{account.srNo}</TableCell>
              <TableCell>{account.accountNumber}</TableCell>
              <TableCell>{account.holderName}</TableCell>
              <TableCell>{account.creationDate}</TableCell>
              <TableCell>${account.balance.toFixed(2)}</TableCell>
              <TableCell>
                <ActionLink>
                  <FaEye />
                  View Balance
                </ActionLink>
              </TableCell>
              <TableCell>
                <ActionLink>
                  <FaEye />
                  View TRansactions
                </ActionLink>
              </TableCell>
            </TableRow>
          ))}
        </tbody>
      </Table>
    </ProfileContainer>
  );
}

export default Profile;

Enter fullscreen mode Exit fullscreen mode

--------------- CREATE ACCOUNT STYLES------------

import styled from 'styled-components';
import { motion, AnimatePresence } from 'framer-motion';

// Styled Components
const Container = styled(motion.div)`
  padding: 32px;
  max-width: 1200px;
  margin: 0 auto;
  min-height: calc(100vh - 120px);

  h2 {
    font-size: 32px;
    font-weight: 800;
    color: #1a202c;
    margin-bottom: 32px;
    background: linear-gradient(135deg, #667eea, #764ba2);
    -webkit-background-clip: text;
    -webkit-text-fill-color: transparent;
    background-clip: text;
    text-align: center;
  }

  @media (max-width: 768px) {
    padding: 20px;

    h2 {
      font-size: 28px;
      margin-bottom: 24px;
    }
  }
`;

const Form = styled(motion.form)`
  background: rgba(255, 255, 255, 0.95);
  backdrop-filter: blur(20px);
  border-radius: 20px;
  padding: 32px;
  margin-bottom: 32px;
  box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
  border: 1px solid rgba(255, 255, 255, 0.2);
  display: grid;
  grid-template-columns: 1fr 1fr auto;
  gap: 24px;
  align-items: end;
  position: relative;
  overflow: hidden;

  &::before {
    content: '';
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    height: 4px;
    background: linear-gradient(135deg, #667eea, #764ba2);
  }

  @media (max-width: 768px) {
    grid-template-columns: 1fr;
    gap: 20px;
    padding: 24px;
  }
`;

const InputGroup = styled.div`
  display: flex;
  flex-direction: column;
  gap: 8px;
`;

const Label = styled.label`
  font-weight: 600;
  color: #4a5568;
  font-size: 14px;
  text-transform: uppercase;
  letter-spacing: 0.5px;
`;

const Input = styled.input`
  padding: 16px 20px;
  border: 2px solid rgba(102, 126, 234, 0.2);
  border-radius: 12px;
  font-size: 16px;
  font-weight: 500;
  color: #2d3748;
  background: rgba(255, 255, 255, 0.8);
  backdrop-filter: blur(10px);
  transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);

  &:focus {
    outline: none;
    border-color: #667eea;
    box-shadow: 0 0 0 4px rgba(102, 126, 234, 0.1);
    background: rgba(255, 255, 255, 0.95);
  }

  &::placeholder {
    color: #a0aec0;
  }

  &:invalid {
    border-color: #ff6b6b;
  }
`;

const Select = styled.select`
  padding: 16px 20px;
  border: 2px solid rgba(102, 126, 234, 0.2);
  border-radius: 12px;
  font-size: 16px;
  font-weight: 500;
  color: #2d3748;
  background: rgba(255, 255, 255, 0.8);
  backdrop-filter: blur(10px);
  transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
  cursor: pointer;

  &:focus {
    outline: none;
    border-color: #667eea;
    box-shadow: 0 0 0 4px rgba(102, 126, 234, 0.1);
    background: rgba(255, 255, 255, 0.95);
  }

  option {
    background: white;
    color: #2d3748;
    padding: 12px;
  }
`;

const CreateButton = styled(motion.button)`
  display: flex;
  align-items: center;
  gap: 12px;
  background: linear-gradient(135deg, #667eea, #764ba2);
  color: white;
  border: none;
  border-radius: 12px;
  padding: 16px 24px;
  font-size: 16px;
  font-weight: 600;
  cursor: pointer;
  transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
  box-shadow: 0 4px 16px rgba(102, 126, 234, 0.4);
  height: fit-content;

  &:hover:not(:disabled) {
    transform: translateY(-2px);
    box-shadow: 0 8px 25px rgba(102, 126, 234, 0.5);
    background: linear-gradient(135deg, #5a67d8, #6b46c1);
  }

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

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

  svg {
    transition: transform 0.2s ease;
  }

  &:hover svg {
    transform: rotate(90deg);
  }
`;

const Table = styled.table`
  width: 100%;
  background: rgba(255, 255, 255, 0.95);
  backdrop-filter: blur(20px);
  border-radius: 20px;
  overflow: hidden;
  box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
  border: 1px solid rgba(255, 255, 255, 0.2);
  border-collapse: separate;
  border-spacing: 0;

  th {
    background: linear-gradient(135deg, #667eea, #764ba2);
    color: white;
    padding: 20px 16px;
    text-align: left;
    font-weight: 600;
    font-size: 14px;
    text-transform: uppercase;
    letter-spacing: 0.5px;
    border: none;

    &:first-child {
      border-top-left-radius: 20px;
    }

    &:last-child {
      border-top-right-radius: 20px;
    }
  }

  td {
    padding: 20px 16px;
    color: #4a5568;
    font-weight: 500;
    border-bottom: 1px solid rgba(226, 232, 240, 0.5);
    vertical-align: middle;
  }

  tr:hover {
    background: linear-gradient(135deg, rgba(102, 126, 234, 0.05), rgba(118, 75, 162, 0.05));
  }

  tr:nth-child(even) {
    background: rgba(248, 250, 252, 0.5);
  }

  tr:last-child td:first-child {
    border-bottom-left-radius: 20px;
  }

  tr:last-child td:last-child {
    border-bottom-right-radius: 20px;
  }

  @media (max-width: 768px) {
    font-size: 14px;

    th, td {
      padding: 16px 12px;
    }
  }
`;

const StatusBadge = styled.span`
  padding: 6px 12px;
  border-radius: 20px;
  font-size: 12px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.5px;

  ${props => props.status === 'ACTIVE' && `
    background: linear-gradient(135deg, rgba(72, 187, 120, 0.2), rgba(56, 178, 172, 0.2));
    color: #38a169;
    border: 1px solid rgba(72, 187, 120, 0.3);
  `}

  ${props => props.status === 'INACTIVE' && `
    background: linear-gradient(135deg, rgba(255, 107, 107, 0.2), rgba(238, 90, 82, 0.2));
    color: #e53e3e;
    border: 1px solid rgba(255, 107, 107, 0.3);
  `}
`;

const AccountTypeBadge = styled.span`
  padding: 6px 12px;
  border-radius: 20px;
  font-size: 12px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.5px;

  ${props => props.type === 'SAVINGS' && `
    background: linear-gradient(135deg, rgba(102, 126, 234, 0.2), rgba(118, 75, 162, 0.2));
    color: #667eea;
    border: 1px solid rgba(102, 126, 234, 0.3);
  `}

  ${props => props.type === 'CURRENT' && `
    background: linear-gradient(135deg, rgba(255, 193, 7, 0.2), rgba(255, 152, 0, 0.2));
    color: #f6ad55;
    border: 1px solid rgba(255, 193, 7, 0.3);
  `}

  ${props => props.type === 'SALARY' && `
    background: linear-gradient(135deg, rgba(72, 187, 120, 0.2), rgba(56, 178, 172, 0.2));
    color: #38a169;
    border: 1px solid rgba(72, 187, 120, 0.3);
  `}
`;

// Modal Components
const ModalOverlay = styled(motion.div)`
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background: rgba(0, 0, 0, 0.6);
  backdrop-filter: blur(8px);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 1000;
  padding: 20px;
`;

const ModalContent = styled(motion.div)`
  background: rgba(255, 255, 255, 0.95);
  backdrop-filter: blur(20px);
  border-radius: 24px;
  padding: 32px;
  max-width: 500px;
  width: 100%;
  box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
  border: 1px solid rgba(255, 255, 255, 0.2);
  position: relative;
  overflow: hidden;

  &::before {
    content: '';
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    height: 4px;
    background: linear-gradient(135deg, #667eea, #764ba2);
  }
`;

const ModalHeader = styled.div`
  margin-bottom: 24px;

  h3 {
    font-size: 24px;
    font-weight: 700;
    color: #1a202c;
    margin-bottom: 8px;
  }

  p {
    color: #4a5568;
    font-size: 16px;
    line-height: 1.5;
  }
`;

const ModalActions = styled.div`
  display: flex;
  gap: 12px;
  justify-content: flex-end;
  margin-top: 32px;
`;

const ModalButton = styled(motion.button)`
  padding: 12px 24px;
  border-radius: 12px;
  font-weight: 600;
  cursor: pointer;
  transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
  border: 2px solid transparent;

  ${props => props.variant === 'primary' ? `
    background: linear-gradient(135deg, #667eea, #764ba2);
    color: white;
    box-shadow: 0 4px 16px rgba(102, 126, 234, 0.4);

    &:hover {
      transform: translateY(-2px);
      box-shadow: 0 8px 25px rgba(102, 126, 234, 0.5);
    }
  ` : `
    background: transparent;
    color: #4a5568;
    border-color: #e2e8f0;

    &:hover {
      background: #f7fafc;
      border-color: #cbd5e0;
    }
  `}

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

// Toast Components
const ToastContainer = styled(motion.div)`
  position: fixed;
  top: 20px;
  right: 20px;
  z-index: 1100;
`;

const Toast = styled(motion.div)`
  background: rgba(255, 255, 255, 0.95);
  backdrop-filter: blur(20px);
  border-radius: 12px;
  padding: 16px 20px;
  margin-bottom: 12px;
  box-shadow: 0 8px 32px rgba(0, 0, 0, 0.15);
  border: 1px solid rgba(255, 255, 255, 0.2);
  display: flex;
  align-items: center;
  gap: 12px;
  min-width: 300px;
  position: relative;
  overflow: hidden;

  &::before {
    content: '';
    position: absolute;
    top: 0;
    left: 0;
    bottom: 0;
    width: 4px;
    background: ${props => 
      props.type === 'success' ? 'linear-gradient(135deg, #48bb78, #38b2ac)' :
      props.type === 'error' ? 'linear-gradient(135deg, #ff6b6b, #ee5a52)' :
      'linear-gradient(135deg, #667eea, #764ba2)'
    };
  }

  .toast-content {
    flex: 1;

    .toast-title {
      font-weight: 600;
      color: #1a202c;
      margin-bottom: 4px;
    }

    .toast-message {
      color: #4a5568;
      font-size: 14px;
    }
  }
`;

// Animation variants
const containerVariants = {
  initial: { opacity: 0, y: 20 },
  animate: { 
    opacity: 1, 
    y: 0, 
    transition: { duration: 0.6, ease: "easeOut" }
  }
};

const formVariants = {
  initial: { opacity: 0, scale: 0.95 },
  animate: { 
    opacity: 1, 
    scale: 1,
    transition: { duration: 0.5, ease: "easeOut" }
  }
};

const modalVariants = {
  overlay: {
    initial: { opacity: 0 },
    animate: { opacity: 1 },
    exit: { opacity: 0 }
  },
  content: {
    initial: { opacity: 0, scale: 0.95, y: 20 },
    animate: { 
      opacity: 1, 
      scale: 1, 
      y: 0,
      transition: { duration: 0.3, ease: "easeOut" }
    },
    exit: { 
      opacity: 0, 
      scale: 0.95, 
      y: 20,
      transition: { duration: 0.2 }
    }
  }
};

const toastVariants = {
  initial: { opacity: 0, x: 300, scale: 0.9 },
  animate: { 
    opacity: 1, 
    x: 0, 
    scale: 1,
    transition: { duration: 0.4, ease: "easeOut" }
  },
  exit: { 
    opacity: 0, 
    x: 300, 
    scale: 0.9,
    transition: { duration: 0.3 }
  }
};

// Reusable Modal Component
const Modal = ({ isOpen, onClose, title, message, onConfirm, confirmText = "Confirm", cancelText = "Cancel" }) => (
  <AnimatePresence>
    {isOpen && (
      <ModalOverlay
        variants={modalVariants.overlay}
        initial="initial"
        animate="animate"
        exit="exit"
        onClick={onClose}
      >
        <ModalContent
          variants={modalVariants.content}
          onClick={e => e.stopPropagation()}
        >
          <ModalHeader>
            <h3>{title}</h3>
            <p>{message}</p>
          </ModalHeader>
          <ModalActions>
            <ModalButton
              variant="secondary"
              onClick={onClose}
              whileHover={{ scale: 1.02 }}
              whileTap={{ scale: 0.98 }}
            >
              {cancelText}
            </ModalButton>
            <ModalButton
              variant="primary"
              onClick={onConfirm}
              whileHover={{ scale: 1.02 }}
              whileTap={{ scale: 0.98 }}
            >
              {confirmText}
            </ModalButton>
          </ModalActions>
        </ModalContent>
      </ModalOverlay>
    )}
  </AnimatePresence>
);

// Reusable Toast Component
const ToastComponent = ({ toasts, removeToast }) => (
  <ToastContainer>
    <AnimatePresence>
      {toasts.map(toast => (
        <Toast
          key={toast.id}
          type={toast.type}
          variants={toastVariants}
          initial="initial"
          animate="animate"
          exit="exit"
          onClick={() => removeToast(toast.id)}
          style={{ cursor: 'pointer' }}
        >
          <div className="toast-content">
            <div className="toast-title">{toast.title}</div>
            <div className="toast-message">{toast.message}</div>
          </div>
        </Toast>
      ))}
    </AnimatePresence>
  </ToastContainer>
);

// Export all styled components for use in the main component
export {
  Container,
  Form,
  InputGroup,
  Label,
  Input,
  Select,
  CreateButton,
  Table,
  StatusBadge,
  AccountTypeBadge,
  Modal,
  ToastComponent,
  containerVariants,
  formVariants
};
Enter fullscreen mode Exit fullscreen mode

--------------- CREATE ACCOUNT PAGE --------------

import { Plus } from "lucide-react";
import { useState, useCallback } from "react";
import {
  Container,
  Form,
  InputGroup,
  Label,
  Input,
  Select,
  CreateButton,
  Table,
  StatusBadge,
  AccountTypeBadge,
  Modal,
  ToastComponent,
  containerVariants,
  formVariants
} from './CreateAccountStyles'; // Import from the styles file

function CreateAccount() {
  const [form, setForm] = useState({
    holderName: "",
    accountType: "SAVINGS",
  });

  const [accounts, setAccounts] = useState([
    {
      name: "Kishan",
      accountNumber: "12345678901",
      balance: 1000.0,
      creationDate: "2023-01-01",
      accountType: "SAVINGS",
      status: "ACTIVE",
    },
    {
      name: "Tilak",
      accountNumber: "09876543210",
      balance: 2000.0,
      creationDate: "2023-02-01",
      accountType: "CURRENT",
      status: "ACTIVE",
    },
  ]);

  const [isModalOpen, setIsModalOpen] = useState(false);
  const [toasts, setToasts] = useState([]);
  const [isLoading, setIsLoading] = useState(false);
  const [errors, setErrors] = useState({});

  // Generate random 11-digit account number
  const generateAccountNumber = () => {
    return Math.floor(10000000000 + Math.random() * 90000000000).toString();
  };

  // Validate form
  const validateForm = () => {
    const newErrors = {};

    if (!form.holderName.trim()) {
      newErrors.holderName = "Account holder name is required";
    } else if (form.holderName.trim().length < 2) {
      newErrors.holderName = "Name must be at least 2 characters";
    } else if (!/^[a-zA-Z\s]+$/.test(form.holderName.trim())) {
      newErrors.holderName = "Name can only contain letters and spaces";
    }

    if (!form.accountType) {
      newErrors.accountType = "Account type is required";
    }

    setErrors(newErrors);
    return Object.keys(newErrors).length === 0;
  };

  // Handle input changes
  const handleInputChange = (e) => {
    const { name, value } = e.target;
    setForm(prev => ({
      ...prev,
      [name]: value
    }));

    // Clear error when user starts typing
    if (errors[name]) {
      setErrors(prev => ({
        ...prev,
        [name]: ""
      }));
    }
  };

  // Add toast notification
  const addToast = useCallback((type, title, message) => {
    const id = Date.now() + Math.random();
    const newToast = { id, type, title, message };

    setToasts(prev => [...prev, newToast]);

    // Auto remove toast after 5 seconds
    setTimeout(() => {
      removeToast(id);
    }, 5000);
  }, []);

  // Remove toast
  const removeToast = useCallback((id) => {
    setToasts(prev => prev.filter(toast => toast.id !== id));
  }, []);

  // Handle form submission
  const handleSubmit = (e) => {
    e.preventDefault();

    if (!validateForm()) {
      addToast('error', 'Validation Error', 'Please fix the errors and try again');
      return;
    }

    setIsModalOpen(true);
  };

  // Handle account creation confirmation
  const handleConfirmCreate = async () => {
    setIsModalOpen(false);
    setIsLoading(true);

    try {
      // Simulate API call
      await new Promise(resolve => setTimeout(resolve, 1500));

      const newAccount = {
        name: form.holderName.trim(),
        accountNumber: generateAccountNumber(),
        balance: 0.0,
        creationDate: new Date().toISOString().split('T')[0],
        accountType: form.accountType,
        status: "ACTIVE",
      };

      setAccounts(prev => [...prev, newAccount]);

      // Reset form
      setForm({
        holderName: "",
        accountType: "SAVINGS",
      });

      addToast(
        'success',
        'Account Created Successfully!',
        `Account ${newAccount.accountNumber} has been created for ${newAccount.name}`
      );

    } catch (error) {
      addToast('error', 'Creation Failed', 'Failed to create account. Please try again.');
    } finally {
      setIsLoading(false);
    }
  };

  // Format currency
  const formatCurrency = (amount) => {
    return new Intl.NumberFormat('en-US', {
      style: 'currency',
      currency: 'USD'
    }).format(amount);
  };

  // Format date
  const formatDate = (dateString) => {
    return new Date(dateString).toLocaleDateString('en-US', {
      year: 'numeric',
      month: 'short',
      day: 'numeric'
    });
  };

  return (
    <>
      <Container
        variants={containerVariants}
        initial="initial"
        animate="animate"
      >
        <h2>Create New Account</h2>

        <Form
          variants={formVariants}
          onSubmit={handleSubmit}
        >
          <InputGroup>
            <Label htmlFor="holderName">Account Holder Name</Label>
            <Input
              id="holderName"
              name="holderName"
              type="text"
              placeholder="Enter full name"
              value={form.holderName}
              onChange={handleInputChange}
              required
              autoComplete="name"
              style={{
                borderColor: errors.holderName ? '#ff6b6b' : undefined
              }}
            />
            {errors.holderName && (
              <span style={{ color: '#ff6b6b', fontSize: '14px', marginTop: '4px' }}>
                {errors.holderName}
              </span>
            )}
          </InputGroup>

          <InputGroup>
            <Label htmlFor="accountType">Account Type</Label>
            <Select
              id="accountType"
              name="accountType"
              value={form.accountType}
              onChange={handleInputChange}
              required
            >
              <option value="SAVINGS">Savings Account</option>
              <option value="CURRENT">Current Account</option>
              <option value="SALARY">Salary Account</option>
            </Select>
          </InputGroup>

          <CreateButton
            type="submit"
            disabled={isLoading}
            whileHover={{ scale: 1.02 }}
            whileTap={{ scale: 0.98 }}
          >
            <Plus size={18} />
            {isLoading ? 'Creating...' : 'Create Account'}
          </CreateButton>
        </Form>

        <Table>
          <thead>
            <tr>
              <th>Account Holder</th>
              <th>Account Number</th>
              <th>Account Type</th>
              <th>Balance</th>
              <th>Creation Date</th>
              <th>Status</th>
            </tr>
          </thead>
          <tbody>
            {accounts.map((account, index) => (
              <tr key={account.accountNumber}>
                <td>{account.name}</td>
                <td>
                  <code style={{ 
                    background: 'rgba(102, 126, 234, 0.1)', 
                    padding: '4px 8px', 
                    borderRadius: '6px',
                    fontSize: '14px',
                    fontWeight: '600'
                  }}>
                    {account.accountNumber}
                  </code>
                </td>
                <td>
                  <AccountTypeBadge type={account.accountType}>
                    {account.accountType}
                  </AccountTypeBadge>
                </td>
                <td style={{ fontWeight: '600', color: '#2d3748' }}>
                  {formatCurrency(account.balance)}
                </td>
                <td>{formatDate(account.creationDate)}</td>
                <td>
                  <StatusBadge status={account.status}>
                    {account.status}
                  </StatusBadge>
                </td>
              </tr>
            ))}
          </tbody>
        </Table>
      </Container>

      <Modal
        isOpen={isModalOpen}
        onClose={() => setIsModalOpen(false)}
        title="Confirm Account Creation"
        message={`Are you sure you want to create a ${form.accountType.toLowerCase()} account for ${form.holderName}? This action cannot be undone.`}
        onConfirm={handleConfirmCreate}
        confirmText="Create Account"
        cancelText="Cancel"
      />

      <ToastComponent 
        toasts={toasts} 
        removeToast={removeToast} 
      />
    </>
  );
}

export default CreateAccount;
Enter fullscreen mode Exit fullscreen mode

Top comments (0)