Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 42 additions & 20 deletions src/Pages/Register.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ function SignupPage() {
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [confirmPassword, setConfirmPassword] = useState("");
const [showPassword, setShowPassword] = useState(false);
const [showConfirmPassword, setShowConfirmPassword] = useState(false);
const [acceptTerms, setAcceptTerms] = useState(false);
const [isLoading, setIsLoading] = useState(false);

Expand All @@ -39,8 +41,8 @@ function SignupPage() {
// Password validation function

const validatePassword = (password) => {
const regex =
/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{6,}$/;
// Allow the '#' character as a valid special character in addition to existing ones
const regex = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&#])[A-Za-z\d@$!%*?&#]{6,}$/;
return regex.test(password);
};

Expand Down Expand Up @@ -213,15 +215,25 @@ function SignupPage() {
>
Password
</label>
<input
type="password"
id="password"
placeholder="Enter your password (min 6 characters)"
className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-[#6D4C41] disabled:bg-gray-100 disabled:cursor-not-allowed"
value={password}
onChange={(e) => setPassword(e.target.value)}
disabled={loading || isLoading}
/>
<div className="relative">
<input
type={showPassword ? 'text' : 'password'}
id="password"
placeholder="Enter your password (min 6 characters)"
className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-[#6D4C41] disabled:bg-gray-100 disabled:cursor-not-allowed"
value={password}
onChange={(e) => setPassword(e.target.value)}
disabled={loading || isLoading}
/>
<button
type="button"
onClick={() => setShowPassword((s) => !s)}
className="absolute right-2 top-1/2 -translate-y-1/2 text-sm text-gray-600 hover:text-gray-800 px-2 py-1"
aria-label={showPassword ? 'Hide password' : 'Show password'}
>
{showPassword ? 'Hide' : 'Show'}
</button>
</div>
{/* βœ… Password rules live checker */}
{password && <PasswordChecklist password={password} />}
</div>
Expand All @@ -233,15 +245,25 @@ function SignupPage() {
>
Confirm Password
</label>
<input
type="password"
id="confirmPassword"
placeholder="Confirm your password"
className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-[#6D4C41] disabled:bg-gray-100 disabled:cursor-not-allowed"
value={confirmPassword}
onChange={(e) => setConfirmPassword(e.target.value)}
disabled={loading || isLoading}
/>
<div className="relative">
<input
type={showConfirmPassword ? 'text' : 'password'}
id="confirmPassword"
placeholder="Confirm your password"
className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-[#6D4C41] disabled:bg-gray-100 disabled:cursor-not-allowed"
value={confirmPassword}
onChange={(e) => setConfirmPassword(e.target.value)}
disabled={loading || isLoading}
/>
<button
type="button"
onClick={() => setShowConfirmPassword((s) => !s)}
className="absolute right-2 top-1/2 -translate-y-1/2 text-sm text-gray-600 hover:text-gray-800 px-2 py-1"
aria-label={showConfirmPassword ? 'Hide confirm password' : 'Show confirm password'}
>
{showConfirmPassword ? 'Hide' : 'Show'}
</button>
</div>
</div>

{/* Terms and Conditions */}
Expand Down
92 changes: 76 additions & 16 deletions src/componets/Navbar.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,22 @@ import { useSelector, useDispatch } from "react-redux";
import { logout } from "../Store/authSlice";
import styled from "styled-components";
import { motion, AnimatePresence } from "framer-motion";
import { FaCoffee, FaUser, FaShoppingCart, FaBars, FaTimes } from "react-icons/fa";
import { FaCoffee, FaUser, FaShoppingCart, FaBars, FaTimes, FaSun, FaMoon } from "react-icons/fa";

const NavbarContainer = styled(motion.nav)`
display: flex;
justify-content: space-between;
align-items: center;
padding: 1.5rem 5%;
background: linear-gradient(135deg, rgba(44, 19, 11, 0.98) 0%, rgba(66, 33, 11, 0.98) 100%);
background: var(--nav-bg);
backdrop-filter: blur(10px);
box-shadow: 0 4px 30px rgba(0, 0, 0, 0.3);
position: fixed;
top: 0;
left: 0;
right: 0;
z-index: 998;
border-bottom: 1px solid rgba(210, 105, 30, 0.3);
border-bottom: 1px solid var(--border);
transition: all 0.3s ease;

&.scrolled {
Expand Down Expand Up @@ -70,7 +70,7 @@ const NavItem = styled(motion.div)`
padding: 0.5rem 0;

a,span {
color: #e6d5b8;
color: var(--text);
font-family: 'Poppins', sans-serif;
font-weight: 500;
font-size: 1.1rem;
Expand All @@ -83,7 +83,7 @@ const NavItem = styled(motion.div)`
padding: 0.5rem 0;

&:hover {
color: #f4a460;
color: var(--accent);
}

&::after {
Expand All @@ -103,7 +103,7 @@ const NavItem = styled(motion.div)`
}

&.active a {
color: #d2691e;
color: var(--primary);
font-weight: 600;

&::after {
Expand All @@ -121,7 +121,7 @@ const DropdownMenu = styled(motion.div)`
padding: 1rem;
min-width: 200px;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3);
border: 1px solid rgba(210, 105, 30, 0.3);
border: 1px solid var(--border);
display: none;
flex-direction: column;
gap: 0.8rem;
Expand All @@ -133,7 +133,7 @@ const DropdownMenu = styled(motion.div)`
transition: all 0.3s ease;

&:hover {
background: rgba(210, 105, 30, 0.2);
background: rgba(210, 105, 30, 0.12);
transform: translateX(5px);
}
}
Expand Down Expand Up @@ -168,17 +168,17 @@ const AuthButton = styled(motion.button)`

&.login {
background: transparent;
color: #e6d5b8;
border: 1px solid #d2691e;
color: var(--text);
border: 1px solid var(--primary);

&:hover {
background: rgba(210, 105, 30, 0.2);
background: rgba(210, 105, 30, 0.12);
}
}

&.register {
background: linear-gradient(to right, #d2691e, #cd853f);
color: white;
background: linear-gradient(to right, var(--primary), #cd853f);
color: var(--surface);
box-shadow: 0 4px 15px rgba(210, 105, 30, 0.4);

&:hover {
Expand All @@ -189,15 +189,29 @@ const AuthButton = styled(motion.button)`

&.logout {
background: transparent;
color: #e6d5b8;
border: 1px solid #8b0000;
color: var(--text);
border: 1px solid rgba(139, 0, 0, 0.25);

&:hover {
background: rgba(139, 0, 0, 0.2);
background: rgba(139, 0, 0, 0.08);
}
}
`;

const ThemeToggleButton = styled(motion.button)`
display: inline-flex;
align-items: center;
justify-content: center;
width: 40px;
height: 40px;
border-radius: 8px;
border: 1px solid var(--border);
background: transparent;
color: var(--text);
cursor: pointer;
padding: 0;
`;

const MobileMenuButton = styled(motion.button)`
display: none;
background: transparent;
Expand Down Expand Up @@ -291,6 +305,14 @@ function Navbar() {
const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false);
const [scrolled, setScrolled] = useState(false);
const [activeDropdown, setActiveDropdown] = useState(null);
// Theme state: we reflect the presence of the `.dark` class on the <html> element
const [isDark, setIsDark] = useState(() => {
try {
return document.documentElement.classList.contains('dark');
} catch (e) {
return false;
}
});

useEffect(() => {
const handleScroll = () => {
Expand All @@ -309,6 +331,22 @@ function Navbar() {
}
}, [isMobileMenuOpen]);

// Toggle theme and persist preference
const toggleTheme = () => {
try {
const willBeDark = !document.documentElement.classList.contains('dark');
if (willBeDark) {
document.documentElement.classList.add('dark');
} else {
document.documentElement.classList.remove('dark');
}
localStorage.setItem('themeMode', willBeDark ? 'dark' : 'light');
setIsDark(willBeDark);
} catch (e) {
// ignore
}
};

const handleLogout = () => {
dispatch(logout());
navigate("/");
Expand Down Expand Up @@ -407,6 +445,16 @@ function Navbar() {
</NavLinks>

<AuthButtons>
<ThemeToggleButton
onClick={toggleTheme}
aria-label="Toggle theme"
aria-pressed={isDark}
whileHover={{ scale: 1.05 }}
whileTap={{ scale: 0.95 }}
title={isDark ? 'Switch to light mode' : 'Switch to dark mode'}
>
{isDark ? <FaSun /> : <FaMoon />}
</ThemeToggleButton>
{isLoggedIn ? (
<>
<NavItem>
Expand Down Expand Up @@ -470,6 +518,18 @@ function Navbar() {
exit={{ opacity: 0, x: "100%" }}
transition={{ type: "tween", duration: 0.3 }}
>
<div style={{ display: 'flex', justifyContent: 'flex-end', marginBottom: '1rem' }}>
<ThemeToggleButton
onClick={() => { toggleTheme(); setIsMobileMenuOpen(false); }}
aria-label="Toggle theme"
aria-pressed={isDark}
whileHover={{ scale: 1.05 }}
whileTap={{ scale: 0.95 }}
title={isDark ? 'Switch to light mode' : 'Switch to dark mode'}
>
{isDark ? <FaSun /> : <FaMoon />}
</ThemeToggleButton>
</div>
{navItems.map((item) => (
<React.Fragment key={item.path || item.title}>
<MobileNavItem
Expand Down
42 changes: 38 additions & 4 deletions src/index.css
Original file line number Diff line number Diff line change
@@ -1,13 +1,47 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
/* Theme variables: light defaults, dark overrides below */
:root {
/* Backgrounds and surfaces */
--bg: #FAF9F6; /* off-white */
--surface: #FFFFFF;

/* Text */
--text: #111827; /* very dark gray */
--muted: #6B7280;

/* Accent / brand */
--primary: #d2691e; /* coffee brown */
--accent: #f4a460;

/* Borders and UI */
--border: rgba(0,0,0,0.08);
--nav-bg: linear-gradient(135deg, rgba(44, 19, 11, 0.98) 0%, rgba(66, 33, 11, 0.98) 100%);
--nav-surface: rgba(44, 19, 11, 0.98);
}

/* Dark theme overrides */
.dark {
--bg: #0b1220; /* dark navy */
--surface: #0f1724;
--text: #E6E6E6;
--muted: #9CA3AF;
--primary: #D9A15A; /* lighter accent for dark */
--accent: #f4a460;
--border: rgba(255,255,255,0.06);
--nav-bg: linear-gradient(135deg, rgba(6, 7, 14, 0.85) 0%, rgba(16, 20, 30, 0.85) 100%);
--nav-surface: rgba(6, 7, 14, 0.9);
}
body {
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
background-color: var(--bg);
color: var(--text);
}

code {
Expand All @@ -22,8 +56,8 @@ code {
bottom: 20px;
right: 20px;
z-index: 99;
background-color: #6F4E37; /* Coffee Brown */
color: #FAF9F6; /* Off-White */
background-color: var(--primary);
color: var(--surface);
border: none;
outline: none;
cursor: pointer;
Expand All @@ -36,8 +70,8 @@ code {

/* Hover effect */
#scrollToTop:hover {
background-color: #D4A373; /* Golden Yellow */
color: #4B4B4B; /* Charcoal Gray */
background-color: var(--accent);
color: var(--text);
transform: scale(1.1); /* Slightly enlarge the button */
}

12 changes: 12 additions & 0 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,18 @@ import ReactDOM from "react-dom/client";
import "./index.css";
import App from "./App";

// Apply saved theme (light/dark) from localStorage before React mounts.
// Requirement: default theme remains unchanged unless user toggles.
try {
const saved = localStorage.getItem('themeMode');
if (saved === 'dark') {
document.documentElement.classList.add('dark');
} else if (saved === 'light') {
document.documentElement.classList.remove('dark');
}
} catch (e) {
// ignore (e.g. SSR or restricted env)
}

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
Expand Down