redesign the website
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful

This commit is contained in:
dvirlabs 2026-06-04 01:58:56 +03:00
parent f02f8106f9
commit dac10f2a26
28 changed files with 1769 additions and 2015 deletions

View File

@ -8,16 +8,26 @@ export default function Footer() {
<div className="footer-container"> <div className="footer-container">
<div className="footer-section"> <div className="footer-section">
<div className="footer-brand"> <div className="footer-brand">
<span className="footer-logo">👟</span> <span className="footer-logo">BM</span>
<h3>Brand Master</h3> <h3>Brand Master</h3>
</div> </div>
<p>Your ultimate destination for fashion and footwear.</p> <p>Premium sneaker boutique for collectors, fashion leaders, and modern street luxury.</p>
<div className="social-links"> <div className="social-links">
<a href="https://instagram.com" target="_blank" rel="noopener noreferrer" title="Instagram">📷</a> <a href="https://instagram.com" target="_blank" rel="noopener noreferrer" title="Instagram">Instagram</a>
<a href="https://wa.me/972532441361" target="_blank" rel="noopener noreferrer" title="WhatsApp">💬</a> <a href="https://wa.me/972532441361" target="_blank" rel="noopener noreferrer" title="WhatsApp">WhatsApp</a>
</div> </div>
</div> </div>
<div className="footer-section">
<h4>Quick Links</h4>
<ul>
<li><Link to="/">Home</Link></li>
<li><Link to="/products">Shop</Link></li>
<li><Link to="/sales">Sale</Link></li>
<li><Link to="/wishlist">Wishlist</Link></li>
</ul>
</div>
<div className="footer-section"> <div className="footer-section">
<h4>Shop</h4> <h4>Shop</h4>
<ul> <ul>
@ -30,20 +40,15 @@ export default function Footer() {
</div> </div>
<div className="footer-section"> <div className="footer-section">
<h4>Company</h4> <h4>Customer Service</h4>
<ul> <ul>
<li><Link to="/about">About Us</Link></li> <li><Link to="/about">About Us</Link></li>
<li><Link to="/contact">Contact</Link></li> <li><Link to="/contact">Contact</Link></li>
<li><Link to="/orders">Shipping & Delivery</Link></li>
<li><a href="#">Terms & Conditions</a></li>
<li><a href="#">Privacy Policy</a></li> <li><a href="#">Privacy Policy</a></li>
<li><a href="#">Terms of Service</a></li>
</ul> </ul>
</div> </div>
<div className="footer-section">
<h4>Contact</h4>
<p>Email: info@brandmaster.com</p>
<p>Phone: +972 53-244-1361</p>
</div>
</div> </div>
<div className="footer-bottom"> <div className="footer-bottom">

View File

@ -24,7 +24,11 @@ export default function Navbar() {
<nav className="navbar"> <nav className="navbar">
<div className="navbar-container"> <div className="navbar-container">
<Link to="/" className="navbar-logo"> <Link to="/" className="navbar-logo">
<span className="logo-icon">👟</span> Brand Master <span className="logo-icon">BM</span>
<span>
<small>BRAND MASTER</small>
<strong>Luxury Sneakers</strong>
</span>
</Link> </Link>
<div className="navbar-center"> <div className="navbar-center">
@ -45,18 +49,18 @@ export default function Navbar() {
<div className="navbar-icons"> <div className="navbar-icons">
{token ? ( {token ? (
<> <>
<Link to="/wishlist" className="icon-btn" title="Wishlist"> <Link to="/wishlist" className="icon-btn" title="Wishlist" aria-label="Wishlist">
</Link> </Link>
<Link to="/cart" className="icon-btn cart-btn" title="Cart"> <Link to="/cart" className="icon-btn cart-btn" title="Cart" aria-label="Cart">
🛒 🛒
{cart.length > 0 && <span className="cart-count">{cart.length}</span>} {cart.length > 0 && <span className="cart-count">{cart.length}</span>}
</Link> </Link>
<Link to="/my-messages" className="icon-btn" title="My Messages"> <Link to="/my-messages" className="icon-btn" title="My Messages" aria-label="Messages">
💬
</Link> </Link>
<Link to="/profile" className="icon-btn" title="Profile"> <Link to="/profile" className="icon-btn" title="Profile" aria-label="Profile">
👤
</Link> </Link>
<button onClick={handleLogout} className="btn btn-small"> <button onClick={handleLogout} className="btn btn-small">
Logout Logout
@ -64,10 +68,16 @@ export default function Navbar() {
</> </>
) : ( ) : (
<> <>
<Link to="/cart" className="icon-btn cart-btn" title="Cart"> <Link to="/wishlist" className="icon-btn" title="Wishlist" aria-label="Wishlist">
</Link>
<Link to="/cart" className="icon-btn cart-btn" title="Cart" aria-label="Cart">
🛒 🛒
{cart.length > 0 && <span className="cart-count">{cart.length}</span>} {cart.length > 0 && <span className="cart-count">{cart.length}</span>}
</Link> </Link>
<Link to="/profile" className="icon-btn" title="Profile" aria-label="Profile">
</Link>
<Link to="/login" className="btn btn-small"> <Link to="/login" className="btn btn-small">
Login Login
</Link> </Link>

View File

@ -30,6 +30,7 @@ export default function ProductCard({ product }) {
alt={product.name} alt={product.name}
onError={() => setImageError(true)} onError={() => setImageError(true)}
/> />
<span className="product-wishlist-icon" aria-hidden="true"></span>
{product.is_on_sale && discount > 0 && ( {product.is_on_sale && discount > 0 && (
<div className="discount-badge">{discount}% OFF</div> <div className="discount-badge">{discount}% OFF</div>
)} )}

View File

@ -144,13 +144,12 @@ export default function ProductFilters({ onFilter }) {
<div className="filter-group"> <div className="filter-group">
<label>Price Range</label> <label>Price Range</label>
<div style={{ display: 'flex', gap: '0.5rem', alignItems: 'center' }}> <div className="price-range-inputs">
<input <input
type="number" type="number"
placeholder="Min" placeholder="Min"
value={filters.min_price} value={filters.min_price}
onChange={(e) => handleFilterChange('min_price', e.target.value)} onChange={(e) => handleFilterChange('min_price', e.target.value)}
style={{ width: '80px', padding: '0.5rem' }}
/> />
<span>-</span> <span>-</span>
<input <input
@ -158,7 +157,6 @@ export default function ProductFilters({ onFilter }) {
placeholder="Max" placeholder="Max"
value={filters.max_price} value={filters.max_price}
onChange={(e) => handleFilterChange('max_price', e.target.value)} onChange={(e) => handleFilterChange('max_price', e.target.value)}
style={{ width: '80px', padding: '0.5rem' }}
/> />
</div> </div>
</div> </div>

View File

@ -1,5 +1,5 @@
import React, { useState, useContext } from 'react' import React, { useState } from 'react'
import { AuthContext } from '../context/AuthContext' import { Link } from 'react-router-dom'
import api from '../api' import api from '../api'
import '../styles/global.css' import '../styles/global.css'
@ -38,10 +38,15 @@ export default function SearchBar() {
{showResults && results.length > 0 && ( {showResults && results.length > 0 && (
<div className="search-results"> <div className="search-results">
{results.map((product) => ( {results.map((product) => (
<a key={product.id} href={`/product/${product.id}`} className="search-result-item"> <Link
key={product.id}
to={`/product/${product.id}`}
className="search-result-item"
onClick={() => setShowResults(false)}
>
<span>{product.name}</span> <span>{product.name}</span>
<span>{product.price.toFixed(2)}</span> <span>{product.price.toFixed(2)}</span>
</a> </Link>
))} ))}
</div> </div>
)} )}

View File

@ -35,51 +35,36 @@ export default function Cart() {
) : ( ) : (
<div className="cart-container"> <div className="cart-container">
<div className="cart-items"> <div className="cart-items">
<table className="cart-table"> {cart.map((item, index) => (
<thead> <article key={index} className="cart-item">
<tr> <img src={item.product.images[0]} alt={item.product.name} />
<th>Product</th> <div>
<th>Price</th> <p className="product-name">{item.product.name}</p>
<th>Quantity</th> <p className="brand">{item.product.brand}</p>
<th>Total</th> {item.size && <p className="text-muted">Size: {item.size}</p>}
<th>Action</th> <p className="price">
</tr> <span className="current">{(item.product.discount_price || item.product.price).toFixed(2)}</span>
</thead> </p>
<tbody> </div>
{cart.map((item, index) => ( <div>
<tr key={index}> <div className="quantity-control">
<td> <button onClick={() => updateQuantity(index, item.quantity - 1)}></button>
<div className="cart-item-product"> <span>{item.quantity}</span>
<img src={item.product.images[0]} alt={item.product.name} /> <button onClick={() => updateQuantity(index, item.quantity + 1)}>+</button>
<div> </div>
<p className="product-name">{item.product.name}</p> <p style={{ marginTop: '0.5rem', textAlign: 'right' }}>
{item.size && <p>Size: {item.size}</p>} {((item.product.discount_price || item.product.price) * item.quantity).toFixed(2)}
</div> </p>
</div> <button
</td> className="btn btn-small btn-danger"
<td>{(item.product.discount_price || item.product.price).toFixed(2)}</td> style={{ marginTop: '0.5rem' }}
<td> onClick={() => removeFromCart(index)}
<div className="quantity-control"> >
<button onClick={() => updateQuantity(index, item.quantity - 1)}></button> Remove
<span>{item.quantity}</span> </button>
<button onClick={() => updateQuantity(index, item.quantity + 1)}>+</button> </div>
</div> </article>
</td> ))}
<td>
{((item.product.discount_price || item.product.price) * item.quantity).toFixed(2)}
</td>
<td>
<button
className="btn btn-small btn-danger"
onClick={() => removeFromCart(index)}
>
Remove
</button>
</td>
</tr>
))}
</tbody>
</table>
</div> </div>
<div className="cart-summary"> <div className="cart-summary">
@ -91,7 +76,7 @@ export default function Cart() {
</div> </div>
<div className="summary-row"> <div className="summary-row">
<span>Shipping:</span> <span>Shipping:</span>
<span>$10.00</span> <span>10.00</span>
</div> </div>
<div className="summary-row"> <div className="summary-row">
<span>Tax:</span> <span>Tax:</span>

View File

@ -150,7 +150,7 @@ export default function Checkout() {
<div className="checkout-container"> <div className="checkout-container">
<form onSubmit={handleSubmit} className="checkout-form"> <form onSubmit={handleSubmit} className="checkout-form">
<div className="form-section"> <div className="form-section">
<h2>Contact Information</h2> <h2>Shipping Information</h2>
<div className="form-group"> <div className="form-group">
<label> <label>
@ -201,7 +201,7 @@ export default function Checkout() {
</div> </div>
<div className="form-section"> <div className="form-section">
<h2>Shipping Address</h2> <h2>Address Details</h2>
<div className="form-group"> <div className="form-group">
<label> <label>

View File

@ -97,12 +97,12 @@ export default function Contact() {
<h2>Get in Touch</h2> <h2>Get in Touch</h2>
<div className="info-item"> <div className="info-item">
<h3>📞 Phone</h3> <h3>Phone</h3>
<p>+972 53-244-1361</p> <p>+972 53-244-1361</p>
</div> </div>
<div className="info-item"> <div className="info-item">
<h3> Email</h3> <h3>Email</h3>
<p>info@brandmaster.com</p> <p>info@brandmaster.com</p>
</div> </div>

View File

@ -43,32 +43,56 @@ export default function Home() {
if (loading) return <div className="loading">Loading...</div> if (loading) return <div className="loading">Loading...</div>
const brands = Array.from(
new Set([...featured, ...newArrivals, ...onSale].map((item) => item.brand).filter(Boolean))
).slice(0, 8)
return ( return (
<div className="home"> <div className="home">
{/* Hero Section */}
<section className="hero"> <section className="hero">
<div className="hero-content"> <div className="hero-content">
<h1>Welcome to Brand Master</h1> <span className="kicker">Luxury Sneaker House</span>
<p>Discover the latest in fashion and footwear</p> <h1>Curated Premium Sneakers. Built for Statement.</h1>
<Link to="/products" className="btn btn-large"> <p>
Shop Now Discover exclusive releases, iconic silhouettes, and elevated essentials in a modern black-and-gold retail experience.
</Link> </p>
<div className="hero-cta-row">
<Link to="/products" className="btn btn-large">Shop Now</Link>
<Link to="/sales" className="btn btn-secondary btn-large">Explore Sales</Link>
</div>
</div>
<div className="hero-media">
<img
src="https://images.unsplash.com/photo-1543508282-6319a3e2621f?auto=format&fit=crop&w=1400&q=80"
alt="Luxury sneaker collection"
/>
</div> </div>
</section> </section>
{/* Category Highlights */} <section className="feature-strip">
<section className="section"> <div className="item">
<h2>Shop by Category</h2> <strong>100% Authentic</strong>
<div className="grid"> <span>Verified premium stock</span>
{categories.map((cat) => ( </div>
<CategoryCard key={cat.id} category={cat} /> <div className="item">
))} <strong>Secure Payment</strong>
<span>Safe and encrypted checkout</span>
</div>
<div className="item">
<strong>Free Shipping</strong>
<span>Orders over 299</span>
</div>
<div className="item">
<strong>Easy Returns</strong>
<span>14-day return policy</span>
</div> </div>
</section> </section>
{/* Featured Products */}
<section className="section"> <section className="section">
<h2>Featured Products</h2> <div className="section-header">
<h2>Featured Products</h2>
<p>Hand-picked from this week's premium drops</p>
</div>
<div className="grid"> <div className="grid">
{featured.map((product) => ( {featured.map((product) => (
<ProductCard key={product.id} product={product} /> <ProductCard key={product.id} product={product} />
@ -76,39 +100,70 @@ export default function Home() {
</div> </div>
</section> </section>
{/* New Arrivals */}
<section className="section"> <section className="section">
<h2>New Arrivals</h2> <div className="section-header">
<h2>New Arrivals</h2>
<p>Fresh releases from top designer brands</p>
</div>
<div className="grid"> <div className="grid">
{newArrivals.slice(0, 4).map((product) => ( {newArrivals.slice(0, 8).map((product) => (
<ProductCard key={product.id} product={product} /> <ProductCard key={product.id} product={product} />
))} ))}
</div> </div>
</section> </section>
{/* On Sale */}
<section className="section sale-banner"> <section className="section sale-banner">
<h2>🔥 Limited Time Offers</h2> <h2>Private Sale Collection</h2>
<p>Up to 50% off on selected items</p> <p>Limited-time markdowns across selected statement pieces.</p>
<Link to="/sales" className="btn btn-large"> <Link to="/sales" className="btn btn-large">View All Sales</Link>
View All Sales
</Link>
</section> </section>
{/* Best Sellers */}
<section className="section"> <section className="section">
<h2>Best Sellers</h2> <div className="section-header">
<h2>Best Sellers</h2>
<p>The silhouettes our community keeps coming back for</p>
</div>
<div className="grid"> <div className="grid">
{onSale.slice(0, 4).map((product) => ( {onSale.slice(0, 8).map((product) => (
<ProductCard key={product.id} product={product} /> <ProductCard key={product.id} product={product} />
))} ))}
</div> </div>
</section> </section>
{/* Promo Banner */} <section className="section">
<div className="section-header">
<h2>Shop by Category</h2>
<p>Build your fit by category</p>
</div>
<div className="grid">
{categories.map((cat) => (
<CategoryCard key={cat.id} category={cat} />
))}
</div>
</section>
{brands.length > 0 && (
<section className="section">
<div className="section-header">
<h2>Brands</h2>
<p>Curated premium houses</p>
</div>
<div className="grid">
{brands.map((brand) => (
<div key={brand} className="category-card">
<div style={{ padding: '1.2rem' }}>
<h3>{brand}</h3>
<p>Explore selected {brand} silhouettes in the Brand Master collection.</p>
</div>
</div>
))}
</div>
</section>
)}
<section className="promo-banner"> <section className="promo-banner">
<h3>Subscribe to Our Newsletter</h3> <h3>Join the Private Client List</h3>
<p>Get exclusive deals and updates</p> <p>Get priority access to drops, restocks, and members-only pricing.</p>
<input type="email" placeholder="Enter your email" /> <input type="email" placeholder="Enter your email" />
<button className="btn">Subscribe</button> <button className="btn">Subscribe</button>
</section> </section>

View File

@ -21,6 +21,7 @@ export default function Login() {
const [resetEmail, setResetEmail] = useState('') const [resetEmail, setResetEmail] = useState('')
const [resetPin, setResetPin] = useState('') const [resetPin, setResetPin] = useState('')
const [newPassword, setNewPassword] = useState('') const [newPassword, setNewPassword] = useState('')
const [rememberMe, setRememberMe] = useState(true)
const [resetLoading, setResetLoading] = useState(false) const [resetLoading, setResetLoading] = useState(false)
const [currentUserData, setCurrentUserData] = useState(null) const [currentUserData, setCurrentUserData] = useState(null)
@ -138,50 +139,72 @@ export default function Login() {
return ( return (
<div className="auth-page"> <div className="auth-page">
<div className="auth-container"> <div className="auth-container">
<h1>Login</h1> <div className="auth-visual">
<img
<form onSubmit={handleSubmit}> src="https://images.unsplash.com/photo-1525966222134-fcfa99b8ae77?auto=format&fit=crop&w=1200&q=80"
<div className="form-group"> alt="Premium sneaker"
<label>Email, Username, or Phone</label> />
<input <div className="auth-visual-copy">
type="text" <h3>Welcome Back</h3>
name="identifier" <p>Log in to your account and continue your premium shopping experience.</p>
value={formData.identifier}
onChange={handleChange}
placeholder="Enter email, username, or phone"
required
/>
</div> </div>
</div>
<div className="form-group"> <div className="auth-panel">
<label>Password</label> <h1>Login</h1>
<input
type="password"
name="password"
value={formData.password}
onChange={handleChange}
required
/>
</div>
<div className="forgot-password-link"> <form onSubmit={handleSubmit}>
<button <div className="form-group">
type="button" <label>Email, Username, or Phone</label>
className="link-button" <input
onClick={() => setShowForgotPassword(true)} type="text"
> name="identifier"
Forgot Password? value={formData.identifier}
onChange={handleChange}
placeholder="Enter email, username, or phone"
required
/>
</div>
<div className="form-group">
<label>Password</label>
<input
type="password"
name="password"
value={formData.password}
onChange={handleChange}
required
/>
</div>
<div className="auth-meta">
<label>
<input
type="checkbox"
checked={rememberMe}
onChange={(e) => setRememberMe(e.target.checked)}
/>
Remember Me
</label>
<button
type="button"
className="link-button"
onClick={() => setShowForgotPassword(true)}
>
Forgot Password?
</button>
</div>
<button type="submit" className="btn btn-full" disabled={loading}>
{loading ? 'Logging in...' : 'Login'}
</button> </button>
</div> </form>
<button type="submit" className="btn btn-full" disabled={loading}> <p className="auth-switch">
{loading ? 'Logging in...' : 'Login'} Don't have an account? <Link to="/register">Sign up here</Link>
</button> </p>
</form> </div>
<p className="auth-switch">
Don't have an account? <Link to="/register">Sign up here</Link>
</p>
</div> </div>
{/* Forgot Password Modal */} {/* Forgot Password Modal */}

View File

@ -1,291 +1,125 @@
.messages-page { .messages-page {
min-height: 100vh; padding-top: 1.2rem;
background-color: #f5f5f5;
padding: 2rem 0;
} }
.messages-container { .messages-container {
max-width: 1200px; border: 1px solid rgba(212, 175, 55, 0.2);
margin: 0 auto; background: linear-gradient(135deg, rgba(18, 18, 18, 0.95), rgba(9, 9, 9, 0.95));
padding: 0 1rem; border-radius: 16px;
padding: 1rem;
} }
.messages-header { .messages-header {
margin-bottom: 2rem;
}
.messages-header h1 {
font-size: 2.5rem;
font-weight: bold;
color: #333;
margin-bottom: 0.5rem;
}
.messages-header p {
color: #666;
font-size: 1.1rem;
}
.error-message {
background-color: #fee;
border: 1px solid #fcc;
color: #c33;
padding: 1rem;
border-radius: 8px;
margin-bottom: 1.5rem;
}
.no-messages {
background: white;
border-radius: 12px;
padding: 3rem;
text-align: center;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
.no-messages-icon {
font-size: 4rem;
margin-bottom: 1rem; margin-bottom: 1rem;
} }
.no-messages h3 { .messages-header h1 {
font-size: 1.5rem; font-size: 2rem;
color: #333;
margin-bottom: 0.5rem;
} }
.no-messages p { .messages-header p {
color: #666; color: var(--text-muted);
margin-bottom: 1.5rem;
} }
.messages-list { .messages-list {
display: flex; display: grid;
flex-direction: column; gap: 0.8rem;
gap: 1.5rem;
} }
.message-card { .message-card {
background: white; border: 1px solid rgba(212, 175, 55, 0.18);
border-radius: 12px; border-radius: 12px;
padding: 1.5rem; background: rgba(255, 255, 255, 0.02);
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); padding: 0.95rem;
transition: box-shadow 0.3s ease;
}
.message-card:hover {
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.15);
} }
.message-card-header { .message-card-header {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: flex-start; gap: 0.6rem;
margin-bottom: 1.5rem; margin-bottom: 0.7rem;
padding-bottom: 1rem;
border-bottom: 1px solid #e5e5e5;
}
.message-title-section {
flex: 1;
} }
.message-title-section h3 { .message-title-section h3 {
font-size: 1.4rem; margin-bottom: 0.2rem;
font-weight: 600;
color: #333;
margin: 0 0 0.5rem 0;
} }
.message-date { .message-date {
font-size: 0.9rem; color: var(--text-soft);
color: #888; font-size: 0.85rem;
margin: 0;
}
.status-badge {
padding: 0.5rem 1rem;
border-radius: 20px;
font-size: 0.75rem;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.5px;
}
.message-content {
margin-bottom: 1.5rem;
}
.message-content h4 {
font-size: 1rem;
font-weight: 600;
color: #555;
margin-bottom: 0.75rem;
}
.message-box {
background: #f9f9f9;
border-radius: 8px;
padding: 1.25rem;
border-left: 4px solid #007bff;
}
.message-box p {
margin: 0;
color: #333;
line-height: 1.6;
white-space: pre-wrap;
}
.admin-response {
margin-top: 1.5rem;
padding-top: 1.5rem;
border-top: 2px solid #e5e5e5;
} }
.message-content h4,
.admin-response h4 { .admin-response h4 {
font-size: 1rem; margin-bottom: 0.35rem;
font-weight: 600; color: var(--gold-bright);
color: #10b981;
margin-bottom: 0.75rem;
display: flex;
align-items: center;
} }
.response-icon { .message-box,
display: inline-flex; .response-box,
align-items: center; .message-details {
justify-content: center; border: 1px solid rgba(212, 175, 55, 0.15);
width: 20px; background: rgba(0, 0, 0, 0.35);
height: 20px; border-radius: 10px;
background: #10b981; padding: 0.75rem;
color: white;
border-radius: 50%;
margin-right: 0.5rem;
font-size: 0.75rem;
font-weight: bold;
}
.response-box {
background: #f0fdf4;
border: 1px solid #86efac;
border-radius: 8px;
padding: 1.25rem;
border-left: 4px solid #10b981;
} }
.message-box p,
.response-box p { .response-box p {
margin: 0; color: var(--text-muted);
color: #333;
line-height: 1.6;
white-space: pre-wrap;
}
.waiting-response {
margin-top: 1.5rem;
padding-top: 1.5rem;
border-top: 2px solid #e5e5e5;
} }
.waiting-response p { .waiting-response p {
color: #888; color: var(--text-soft);
font-style: italic; font-style: italic;
margin: 0;
}
.message-details-section {
margin-top: 1.5rem;
padding-top: 1.5rem;
border-top: 1px solid #e5e5e5;
} }
.details-toggle { .details-toggle {
background: none; color: var(--gold-bright);
border: none; margin-top: 0.75rem;
color: #007bff;
font-size: 0.9rem;
cursor: pointer;
padding: 0.5rem 0;
display: flex;
align-items: center;
gap: 0.5rem;
transition: color 0.2s ease;
}
.details-toggle:hover {
color: #0056b3;
}
.message-details {
margin-top: 1rem;
padding: 1rem;
background: #f9f9f9;
border-radius: 8px;
} }
.details-grid { .details-grid {
margin-top: 0.6rem;
display: grid; display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); grid-template-columns: repeat(2, 1fr);
gap: 1rem; gap: 0.5rem;
}
.detail-item {
display: flex;
flex-direction: column;
gap: 0.25rem;
} }
.detail-item strong { .detail-item strong {
font-weight: 600; color: var(--text-main);
color: #555; font-size: 0.86rem;
font-size: 0.9rem;
} }
.detail-item span { .detail-item span {
color: #666; color: var(--text-muted);
font-size: 0.95rem; font-size: 0.86rem;
} }
.messages-actions { .messages-actions {
margin-top: 2rem; margin-top: 0.8rem;
}
.no-messages {
text-align: center; text-align: center;
border: 1px solid rgba(212, 175, 55, 0.2);
border-radius: 12px;
padding: 1.6rem;
background: rgba(255, 255, 255, 0.02);
} }
.btn-secondary { .no-messages-icon {
background-color: #6c757d; font-size: 2rem;
color: white; margin-bottom: 0.4rem;
} }
.btn-secondary:hover { @media (max-width: 720px) {
background-color: #5a6268; .details-grid {
} grid-template-columns: 1fr;
/* Loading state */
.loading {
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
font-size: 1.5rem;
color: #666;
}
/* Responsive design */
@media (max-width: 768px) {
.messages-header h1 {
font-size: 2rem;
} }
.message-card-header { .message-card-header {
flex-direction: column; flex-direction: column;
gap: 1rem; align-items: flex-start;
}
.status-badge {
align-self: flex-start;
}
.details-grid {
grid-template-columns: 1fr;
} }
} }

View File

@ -137,7 +137,7 @@ export default function ProductDetail() {
<p className="brand">{product.brand}</p> <p className="brand">{product.brand}</p>
<div className="rating"> <div className="rating">
<span> (42 reviews)</span> <span> (42 reviews)</span>
</div> </div>
<div className="price"> <div className="price">
@ -151,6 +151,8 @@ export default function ProductDetail() {
)} )}
</div> </div>
<p className="model-code">Model Code: {product.sku || product.id}</p>
<p className="description">{product.description}</p> <p className="description">{product.description}</p>
{product.sizes.length > 0 && ( {product.sizes.length > 0 && (
@ -198,10 +200,11 @@ export default function ProductDetail() {
Add to Cart Add to Cart
</button> </button>
<button <button
className={`btn btn-icon ${inWishlist ? 'active' : ''}`} className={`btn-icon ${inWishlist ? 'active' : ''}`}
onClick={handleToggleWishlist} onClick={handleToggleWishlist}
aria-label="Add to wishlist"
> >
{inWishlist ? '❤️' : '🤍'} {inWishlist ? '♥' : '♡'}
</button> </button>
</div> </div>

View File

@ -110,7 +110,7 @@ export default function Products() {
</span> </span>
<button <button
onClick={() => { setShowAll(s => !s); setCurrentPage(1) }} onClick={() => { setShowAll(s => !s); setCurrentPage(1) }}
style={{ marginLeft: '1rem', padding: '0.3rem 0.8rem', borderRadius: '4px', border: '1px solid #ccc', cursor: 'pointer', fontSize: '0.85rem' }} className="page-btn"
> >
{showAll ? 'Paginate' : 'Show All'} {showAll ? 'Paginate' : 'Show All'}
</button> </button>
@ -128,11 +128,11 @@ export default function Products() {
{/* Pagination controls (hidden when Show All is active) */} {/* Pagination controls (hidden when Show All is active) */}
{!showAll && totalPages > 1 && ( {!showAll && totalPages > 1 && (
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', gap: '0.5rem', marginTop: '2rem', flexWrap: 'wrap' }}> <div className="pagination">
<button <button
onClick={() => setCurrentPage(p => Math.max(1, p - 1))} onClick={() => setCurrentPage(p => Math.max(1, p - 1))}
disabled={currentPage <= 1} disabled={currentPage <= 1}
style={{ padding: '0.5rem 1rem', borderRadius: '4px', border: '1px solid #ccc', cursor: currentPage <= 1 ? 'not-allowed' : 'pointer', opacity: currentPage <= 1 ? 0.5 : 1 }} className="page-btn"
> >
Prev Prev
</button> </button>
@ -143,7 +143,7 @@ export default function Products() {
<button <button
key={page} key={page}
onClick={() => setCurrentPage(page)} onClick={() => setCurrentPage(page)}
style={{ padding: '0.5rem 1rem', borderRadius: '4px', border: '1px solid #ccc', cursor: 'pointer', backgroundColor: page === currentPage ? '#333' : 'white', color: page === currentPage ? 'white' : 'inherit' }} className={`page-btn ${page === currentPage ? 'active' : ''}`}
> >
{page} {page}
</button> </button>
@ -152,7 +152,7 @@ export default function Products() {
<button <button
onClick={() => setCurrentPage(p => Math.min(totalPages, p + 1))} onClick={() => setCurrentPage(p => Math.min(totalPages, p + 1))}
disabled={currentPage >= totalPages} disabled={currentPage >= totalPages}
style={{ padding: '0.5rem 1rem', borderRadius: '4px', border: '1px solid #ccc', cursor: currentPage >= totalPages ? 'not-allowed' : 'pointer', opacity: currentPage >= totalPages ? 0.5 : 1 }} className="page-btn"
> >
Next Next
</button> </button>

View File

@ -1,5 +1,5 @@
import React, { useState, useEffect, useContext } from 'react' import React, { useState, useEffect, useContext } from 'react'
import { useNavigate } from 'react-router-dom' import { Link, useNavigate } from 'react-router-dom'
import api from '../api' import api from '../api'
import { AuthContext } from '../context/AuthContext' import { AuthContext } from '../context/AuthContext'
import Toast from '../components/Toast' import Toast from '../components/Toast'
@ -26,6 +26,12 @@ export default function Profile() {
const [saving, setSaving] = useState(false) const [saving, setSaving] = useState(false)
const [changingPassword, setChangingPassword] = useState(false) const [changingPassword, setChangingPassword] = useState(false)
const [toast, setToast] = useState(null) const [toast, setToast] = useState(null)
const [activeSection, setActiveSection] = useState('overview')
const [stats, setStats] = useState({
orders: 0,
wishlist: 0,
messages: 0,
})
useEffect(() => { useEffect(() => {
if (!token) { if (!token) {
@ -37,9 +43,20 @@ export default function Profile() {
const fetchProfile = async () => { const fetchProfile = async () => {
try { try {
const response = await api.get('/users/me') const [profileRes, ordersRes, wishlistRes, messagesRes] = await Promise.all([
setFormData(response.data) api.get('/users/me'),
setUser(response.data) api.get('/orders').catch(() => ({ data: [] })),
api.get('/wishlist').catch(() => ({ data: [] })),
api.get('/my-messages').catch(() => ({ data: [] })),
])
setFormData(profileRes.data)
setUser(profileRes.data)
setStats({
orders: ordersRes.data.length || 0,
wishlist: wishlistRes.data.length || 0,
messages: messagesRes.data.length || 0,
})
} catch (error) { } catch (error) {
console.error('Error fetching profile:', error) console.error('Error fetching profile:', error)
} finally { } finally {
@ -103,145 +120,178 @@ export default function Profile() {
return ( return (
<div className="profile-page"> <div className="profile-page">
<div style={{ display: 'flex', alignItems: 'center', gap: '1rem', marginBottom: '1rem' }}> <h1>
<h1>My Profile</h1> My Profile
{user?.is_admin && ( {user?.is_admin && <span className="admin-pill">ADMIN</span>}
<span style={{ </h1>
backgroundColor: '#ff6b6b',
color: 'white',
padding: '0.25rem 0.75rem',
borderRadius: '4px',
fontSize: '0.875rem',
fontWeight: 'bold'
}}>
ADMIN
</span>
)}
</div>
<div className="profile-container"> <div className="profile-shell">
<form onSubmit={handleSubmit} className="profile-form"> <nav className="profile-nav">
<h2>Personal Information</h2> <button
className={activeSection === 'overview' ? 'active' : ''}
<div className="form-group"> onClick={() => setActiveSection('overview')}
<label>Full Name</label> type="button"
<input >
type="text" Overview
name="full_name"
value={formData.full_name}
onChange={handleChange}
/>
</div>
<div className="form-group">
<label>Email</label>
<input
type="email"
name="email"
value={formData.email}
onChange={handleChange}
required
/>
</div>
<div className="form-group">
<label>Phone</label>
<input
type="tel"
name="phone"
value={formData.phone || ''}
onChange={handleChange}
/>
</div>
<h2>Shipping Address</h2>
<div className="form-group">
<label>Address</label>
<input
type="text"
name="address"
value={formData.address || ''}
onChange={handleChange}
/>
</div>
<div className="form-row">
<div className="form-group">
<label>City</label>
<input
type="text"
name="city"
value={formData.city || ''}
onChange={handleChange}
/>
</div>
<div className="form-group">
<label>Postal Code</label>
<input
type="text"
name="postal_code"
value={formData.postal_code || ''}
onChange={handleChange}
/>
</div>
<div className="form-group">
<label>Country</label>
<input
type="text"
name="country"
value={formData.country || ''}
onChange={handleChange}
/>
</div>
</div>
<button type="submit" className="btn btn-full" disabled={saving}>
{saving ? 'Saving...' : 'Save Changes'}
</button> </button>
</form> <Link to="/orders">Orders</Link>
<Link to="/wishlist">Wishlist</Link>
<form onSubmit={handlePasswordChange} className="profile-form" style={{ marginTop: '2rem' }}> <Link to="/my-messages">Messages</Link>
<h2>Change Password</h2> <button
className={activeSection === 'addresses' ? 'active' : ''}
<div className="form-group"> onClick={() => setActiveSection('addresses')}
<label>Current Password</label> type="button"
<input >
type="password" Addresses
value={passwordData.old_password}
onChange={(e) => setPasswordData({ ...passwordData, old_password: e.target.value })}
required
/>
</div>
<div className="form-group">
<label>New Password</label>
<input
type="password"
value={passwordData.new_password}
onChange={(e) => setPasswordData({ ...passwordData, new_password: e.target.value })}
minLength="6"
required
/>
</div>
<div className="form-group">
<label>Confirm New Password</label>
<input
type="password"
value={passwordData.confirm_password}
onChange={(e) => setPasswordData({ ...passwordData, confirm_password: e.target.value })}
minLength="6"
required
/>
</div>
<button type="submit" className="btn btn-full" disabled={changingPassword}>
{changingPassword ? 'Changing...' : 'Change Password'}
</button> </button>
</form> <button
className={activeSection === 'settings' ? 'active' : ''}
onClick={() => setActiveSection('settings')}
type="button"
>
Account Settings
</button>
</nav>
<div className="profile-forms">
<div className="profile-metrics">
<div className="profile-metric">
<span>Total Orders</span>
<strong>{stats.orders}</strong>
</div>
<div className="profile-metric">
<span>Wishlist Count</span>
<strong>{stats.wishlist}</strong>
</div>
<div className="profile-metric">
<span>Messages Count</span>
<strong>{stats.messages}</strong>
</div>
</div>
<form onSubmit={handleSubmit} className="profile-form">
<h2>{activeSection === 'addresses' ? 'Addresses' : 'Personal Information'}</h2>
<div className="form-group">
<label>Full Name</label>
<input
type="text"
name="full_name"
value={formData.full_name}
onChange={handleChange}
/>
</div>
<div className="form-group">
<label>Email</label>
<input
type="email"
name="email"
value={formData.email}
onChange={handleChange}
required
/>
</div>
<div className="form-group">
<label>Phone</label>
<input
type="tel"
name="phone"
value={formData.phone || ''}
onChange={handleChange}
/>
</div>
<h2>Shipping Address</h2>
<div className="form-group">
<label>Address</label>
<input
type="text"
name="address"
value={formData.address || ''}
onChange={handleChange}
/>
</div>
<div className="form-row">
<div className="form-group">
<label>City</label>
<input
type="text"
name="city"
value={formData.city || ''}
onChange={handleChange}
/>
</div>
<div className="form-group">
<label>Postal Code</label>
<input
type="text"
name="postal_code"
value={formData.postal_code || ''}
onChange={handleChange}
/>
</div>
<div className="form-group">
<label>Country</label>
<input
type="text"
name="country"
value={formData.country || ''}
onChange={handleChange}
/>
</div>
</div>
<button type="submit" className="btn btn-full" disabled={saving}>
{saving ? 'Saving...' : 'Save Changes'}
</button>
</form>
<form onSubmit={handlePasswordChange} className="profile-form">
<h2>Account Settings</h2>
<div className="form-group">
<label>Current Password</label>
<input
type="password"
value={passwordData.old_password}
onChange={(e) => setPasswordData({ ...passwordData, old_password: e.target.value })}
required
/>
</div>
<div className="form-group">
<label>New Password</label>
<input
type="password"
value={passwordData.new_password}
onChange={(e) => setPasswordData({ ...passwordData, new_password: e.target.value })}
minLength="6"
required
/>
</div>
<div className="form-group">
<label>Confirm New Password</label>
<input
type="password"
value={passwordData.confirm_password}
onChange={(e) => setPasswordData({ ...passwordData, confirm_password: e.target.value })}
minLength="6"
required
/>
</div>
<button type="submit" className="btn btn-full" disabled={changingPassword}>
{changingPassword ? 'Changing...' : 'Change Password'}
</button>
</form>
</div>
</div> </div>
{toast && ( {toast && (

View File

@ -60,73 +60,86 @@ export default function Register() {
return ( return (
<div className="auth-page"> <div className="auth-page">
<div className="auth-container"> <div className="auth-container">
<h1>Create Account</h1> <div className="auth-visual">
<img
<form onSubmit={handleSubmit}> src="https://images.unsplash.com/photo-1515955656352-a1fa3ffcd111?auto=format&fit=crop&w=1200&q=80"
<div className="form-group"> alt="Luxury sneaker"
<label>Full Name</label> />
<input <div className="auth-visual-copy">
type="text" <h3>Create Account</h3>
name="full_name" <p>Join Brand Master and access exclusive collections and member-only drops.</p>
value={formData.full_name}
onChange={handleChange}
required
/>
</div> </div>
</div>
<div className="form-group"> <div className="auth-panel">
<label>Email</label> <h1>Create Account</h1>
<input
type="email"
name="email"
value={formData.email}
onChange={handleChange}
required
/>
</div>
<div className="form-group"> <form onSubmit={handleSubmit}>
<label>Username (Optional)</label> <div className="form-group">
<input <label>Full Name</label>
type="text" <input
name="username" type="text"
value={formData.username} name="full_name"
onChange={handleChange} value={formData.full_name}
placeholder="Choose a unique username" onChange={handleChange}
/> required
</div> />
</div>
<div className="form-group"> <div className="form-group">
<label>Phone (Optional)</label> <label>Email</label>
<input <input
type="tel" type="email"
name="phone" name="email"
value={formData.phone} value={formData.email}
onChange={handleChange} onChange={handleChange}
placeholder="+972 XX-XXX-XXXX" required
/> />
</div> </div>
<div className="form-group"> <div className="form-group">
<label>Password</label> <label>Username (Optional)</label>
<input <input
type="password" type="text"
name="password" name="username"
value={formData.password} value={formData.username}
onChange={handleChange} onChange={handleChange}
minLength="6" placeholder="Choose a unique username"
required />
/> </div>
</div>
<button type="submit" className="btn btn-full" disabled={loading}> <div className="form-group">
{loading ? 'Creating account...' : 'Sign Up'} <label>Phone (Optional)</label>
</button> <input
</form> type="tel"
name="phone"
value={formData.phone}
onChange={handleChange}
placeholder="+972 XX-XXX-XXXX"
/>
</div>
<p className="auth-switch"> <div className="form-group">
Already have an account? <Link to="/login">Login here</Link> <label>Password</label>
</p> <input
type="password"
name="password"
value={formData.password}
onChange={handleChange}
minLength="6"
required
/>
</div>
<button type="submit" className="btn btn-full" disabled={loading}>
{loading ? 'Creating account...' : 'Sign Up'}
</button>
</form>
<p className="auth-switch">
Already have an account? <Link to="/login">Login here</Link>
</p>
</div>
</div> </div>
{toast && ( {toast && (

View File

@ -30,8 +30,8 @@ export default function Sales() {
return ( return (
<div className="sales-page"> <div className="sales-page">
<div className="sales-header"> <div className="sales-header">
<h1>🔥 Limited Time Offers</h1> <h1>Limited Time Offers</h1>
<p>Huge discounts on selected items - Limited time only!</p> <p>Luxury selections with exclusive markdowns. Ends soon.</p>
</div> </div>
{loading ? ( {loading ? (

File diff suppressed because it is too large Load Diff