redesign the website
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
This commit is contained in:
parent
f02f8106f9
commit
dac10f2a26
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -8,16 +8,26 @@ export default function Footer() {
|
||||
<div className="footer-container">
|
||||
<div className="footer-section">
|
||||
<div className="footer-brand">
|
||||
<span className="footer-logo">👟</span>
|
||||
<span className="footer-logo">BM</span>
|
||||
<h3>Brand Master</h3>
|
||||
</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">
|
||||
<a href="https://instagram.com" target="_blank" rel="noopener noreferrer" title="Instagram">📷</a>
|
||||
<a href="https://wa.me/972532441361" target="_blank" rel="noopener noreferrer" title="WhatsApp">💬</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">WhatsApp</a>
|
||||
</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">
|
||||
<h4>Shop</h4>
|
||||
<ul>
|
||||
@ -30,20 +40,15 @@ export default function Footer() {
|
||||
</div>
|
||||
|
||||
<div className="footer-section">
|
||||
<h4>Company</h4>
|
||||
<h4>Customer Service</h4>
|
||||
<ul>
|
||||
<li><Link to="/about">About Us</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="#">Terms of Service</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div className="footer-section">
|
||||
<h4>Contact</h4>
|
||||
<p>Email: info@brandmaster.com</p>
|
||||
<p>Phone: +972 53-244-1361</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="footer-bottom">
|
||||
|
||||
@ -24,7 +24,11 @@ export default function Navbar() {
|
||||
<nav className="navbar">
|
||||
<div className="navbar-container">
|
||||
<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>
|
||||
|
||||
<div className="navbar-center">
|
||||
@ -45,18 +49,18 @@ export default function Navbar() {
|
||||
<div className="navbar-icons">
|
||||
{token ? (
|
||||
<>
|
||||
<Link to="/wishlist" className="icon-btn" title="Wishlist">
|
||||
❤️
|
||||
<Link to="/wishlist" className="icon-btn" title="Wishlist" aria-label="Wishlist">
|
||||
♡
|
||||
</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>}
|
||||
</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 to="/profile" className="icon-btn" title="Profile">
|
||||
👤
|
||||
<Link to="/profile" className="icon-btn" title="Profile" aria-label="Profile">
|
||||
◉
|
||||
</Link>
|
||||
<button onClick={handleLogout} className="btn btn-small">
|
||||
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>}
|
||||
</Link>
|
||||
<Link to="/profile" className="icon-btn" title="Profile" aria-label="Profile">
|
||||
◉
|
||||
</Link>
|
||||
<Link to="/login" className="btn btn-small">
|
||||
Login
|
||||
</Link>
|
||||
|
||||
@ -30,6 +30,7 @@ export default function ProductCard({ product }) {
|
||||
alt={product.name}
|
||||
onError={() => setImageError(true)}
|
||||
/>
|
||||
<span className="product-wishlist-icon" aria-hidden="true">♡</span>
|
||||
{product.is_on_sale && discount > 0 && (
|
||||
<div className="discount-badge">{discount}% OFF</div>
|
||||
)}
|
||||
|
||||
@ -144,13 +144,12 @@ export default function ProductFilters({ onFilter }) {
|
||||
|
||||
<div className="filter-group">
|
||||
<label>Price Range</label>
|
||||
<div style={{ display: 'flex', gap: '0.5rem', alignItems: 'center' }}>
|
||||
<div className="price-range-inputs">
|
||||
<input
|
||||
type="number"
|
||||
placeholder="Min"
|
||||
value={filters.min_price}
|
||||
onChange={(e) => handleFilterChange('min_price', e.target.value)}
|
||||
style={{ width: '80px', padding: '0.5rem' }}
|
||||
/>
|
||||
<span>-</span>
|
||||
<input
|
||||
@ -158,7 +157,6 @@ export default function ProductFilters({ onFilter }) {
|
||||
placeholder="Max"
|
||||
value={filters.max_price}
|
||||
onChange={(e) => handleFilterChange('max_price', e.target.value)}
|
||||
style={{ width: '80px', padding: '0.5rem' }}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import React, { useState, useContext } from 'react'
|
||||
import { AuthContext } from '../context/AuthContext'
|
||||
import React, { useState } from 'react'
|
||||
import { Link } from 'react-router-dom'
|
||||
import api from '../api'
|
||||
import '../styles/global.css'
|
||||
|
||||
@ -38,10 +38,15 @@ export default function SearchBar() {
|
||||
{showResults && results.length > 0 && (
|
||||
<div className="search-results">
|
||||
{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.price.toFixed(2)}</span>
|
||||
</a>
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
@ -35,51 +35,36 @@ export default function Cart() {
|
||||
) : (
|
||||
<div className="cart-container">
|
||||
<div className="cart-items">
|
||||
<table className="cart-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Product</th>
|
||||
<th>Price</th>
|
||||
<th>Quantity</th>
|
||||
<th>Total</th>
|
||||
<th>Action</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{cart.map((item, index) => (
|
||||
<tr key={index}>
|
||||
<td>
|
||||
<div className="cart-item-product">
|
||||
<article key={index} className="cart-item">
|
||||
<img src={item.product.images[0]} alt={item.product.name} />
|
||||
<div>
|
||||
<p className="product-name">{item.product.name}</p>
|
||||
{item.size && <p>Size: {item.size}</p>}
|
||||
<p className="brand">{item.product.brand}</p>
|
||||
{item.size && <p className="text-muted">Size: {item.size}</p>}
|
||||
<p className="price">
|
||||
<span className="current">₪{(item.product.discount_price || item.product.price).toFixed(2)}</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td>₪{(item.product.discount_price || item.product.price).toFixed(2)}</td>
|
||||
<td>
|
||||
<div>
|
||||
<div className="quantity-control">
|
||||
<button onClick={() => updateQuantity(index, item.quantity - 1)}>−</button>
|
||||
<span>{item.quantity}</span>
|
||||
<button onClick={() => updateQuantity(index, item.quantity + 1)}>+</button>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<p style={{ marginTop: '0.5rem', textAlign: 'right' }}>
|
||||
₪{((item.product.discount_price || item.product.price) * item.quantity).toFixed(2)}
|
||||
</td>
|
||||
<td>
|
||||
</p>
|
||||
<button
|
||||
className="btn btn-small btn-danger"
|
||||
style={{ marginTop: '0.5rem' }}
|
||||
onClick={() => removeFromCart(index)}
|
||||
>
|
||||
Remove
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
</div>
|
||||
</article>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div className="cart-summary">
|
||||
@ -91,7 +76,7 @@ export default function Cart() {
|
||||
</div>
|
||||
<div className="summary-row">
|
||||
<span>Shipping:</span>
|
||||
<span>$10.00</span>
|
||||
<span>₪10.00</span>
|
||||
</div>
|
||||
<div className="summary-row">
|
||||
<span>Tax:</span>
|
||||
|
||||
@ -150,7 +150,7 @@ export default function Checkout() {
|
||||
<div className="checkout-container">
|
||||
<form onSubmit={handleSubmit} className="checkout-form">
|
||||
<div className="form-section">
|
||||
<h2>Contact Information</h2>
|
||||
<h2>Shipping Information</h2>
|
||||
|
||||
<div className="form-group">
|
||||
<label>
|
||||
@ -201,7 +201,7 @@ export default function Checkout() {
|
||||
</div>
|
||||
|
||||
<div className="form-section">
|
||||
<h2>Shipping Address</h2>
|
||||
<h2>Address Details</h2>
|
||||
|
||||
<div className="form-group">
|
||||
<label>
|
||||
|
||||
@ -97,12 +97,12 @@ export default function Contact() {
|
||||
<h2>Get in Touch</h2>
|
||||
|
||||
<div className="info-item">
|
||||
<h3>📞 Phone</h3>
|
||||
<h3>Phone</h3>
|
||||
<p>+972 53-244-1361</p>
|
||||
</div>
|
||||
|
||||
<div className="info-item">
|
||||
<h3>✉️ Email</h3>
|
||||
<h3>Email</h3>
|
||||
<p>info@brandmaster.com</p>
|
||||
</div>
|
||||
|
||||
|
||||
@ -43,32 +43,56 @@ export default function Home() {
|
||||
|
||||
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 (
|
||||
<div className="home">
|
||||
{/* Hero Section */}
|
||||
<section className="hero">
|
||||
<div className="hero-content">
|
||||
<h1>Welcome to Brand Master</h1>
|
||||
<p>Discover the latest in fashion and footwear</p>
|
||||
<Link to="/products" className="btn btn-large">
|
||||
Shop Now
|
||||
</Link>
|
||||
<span className="kicker">Luxury Sneaker House</span>
|
||||
<h1>Curated Premium Sneakers. Built for Statement.</h1>
|
||||
<p>
|
||||
Discover exclusive releases, iconic silhouettes, and elevated essentials in a modern black-and-gold retail experience.
|
||||
</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>
|
||||
</section>
|
||||
|
||||
{/* Category Highlights */}
|
||||
<section className="section">
|
||||
<h2>Shop by Category</h2>
|
||||
<div className="grid">
|
||||
{categories.map((cat) => (
|
||||
<CategoryCard key={cat.id} category={cat} />
|
||||
))}
|
||||
<section className="feature-strip">
|
||||
<div className="item">
|
||||
<strong>100% Authentic</strong>
|
||||
<span>Verified premium stock</span>
|
||||
</div>
|
||||
<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>
|
||||
</section>
|
||||
|
||||
{/* Featured Products */}
|
||||
<section className="section">
|
||||
<div className="section-header">
|
||||
<h2>Featured Products</h2>
|
||||
<p>Hand-picked from this week's premium drops</p>
|
||||
</div>
|
||||
<div className="grid">
|
||||
{featured.map((product) => (
|
||||
<ProductCard key={product.id} product={product} />
|
||||
@ -76,39 +100,70 @@ export default function Home() {
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* New Arrivals */}
|
||||
<section className="section">
|
||||
<div className="section-header">
|
||||
<h2>New Arrivals</h2>
|
||||
<p>Fresh releases from top designer brands</p>
|
||||
</div>
|
||||
<div className="grid">
|
||||
{newArrivals.slice(0, 4).map((product) => (
|
||||
{newArrivals.slice(0, 8).map((product) => (
|
||||
<ProductCard key={product.id} product={product} />
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* On Sale */}
|
||||
<section className="section sale-banner">
|
||||
<h2>🔥 Limited Time Offers</h2>
|
||||
<p>Up to 50% off on selected items</p>
|
||||
<Link to="/sales" className="btn btn-large">
|
||||
View All Sales
|
||||
</Link>
|
||||
<h2>Private Sale Collection</h2>
|
||||
<p>Limited-time markdowns across selected statement pieces.</p>
|
||||
<Link to="/sales" className="btn btn-large">View All Sales</Link>
|
||||
</section>
|
||||
|
||||
{/* Best Sellers */}
|
||||
<section className="section">
|
||||
<div className="section-header">
|
||||
<h2>Best Sellers</h2>
|
||||
<p>The silhouettes our community keeps coming back for</p>
|
||||
</div>
|
||||
<div className="grid">
|
||||
{onSale.slice(0, 4).map((product) => (
|
||||
{onSale.slice(0, 8).map((product) => (
|
||||
<ProductCard key={product.id} product={product} />
|
||||
))}
|
||||
</div>
|
||||
</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">
|
||||
<h3>Subscribe to Our Newsletter</h3>
|
||||
<p>Get exclusive deals and updates</p>
|
||||
<h3>Join the Private Client List</h3>
|
||||
<p>Get priority access to drops, restocks, and members-only pricing.</p>
|
||||
<input type="email" placeholder="Enter your email" />
|
||||
<button className="btn">Subscribe</button>
|
||||
</section>
|
||||
|
||||
@ -21,6 +21,7 @@ export default function Login() {
|
||||
const [resetEmail, setResetEmail] = useState('')
|
||||
const [resetPin, setResetPin] = useState('')
|
||||
const [newPassword, setNewPassword] = useState('')
|
||||
const [rememberMe, setRememberMe] = useState(true)
|
||||
const [resetLoading, setResetLoading] = useState(false)
|
||||
const [currentUserData, setCurrentUserData] = useState(null)
|
||||
|
||||
@ -138,6 +139,18 @@ export default function Login() {
|
||||
return (
|
||||
<div className="auth-page">
|
||||
<div className="auth-container">
|
||||
<div className="auth-visual">
|
||||
<img
|
||||
src="https://images.unsplash.com/photo-1525966222134-fcfa99b8ae77?auto=format&fit=crop&w=1200&q=80"
|
||||
alt="Premium sneaker"
|
||||
/>
|
||||
<div className="auth-visual-copy">
|
||||
<h3>Welcome Back</h3>
|
||||
<p>Log in to your account and continue your premium shopping experience.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="auth-panel">
|
||||
<h1>Login</h1>
|
||||
|
||||
<form onSubmit={handleSubmit}>
|
||||
@ -164,7 +177,16 @@ export default function Login() {
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="forgot-password-link">
|
||||
<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"
|
||||
@ -183,6 +205,7 @@ export default function Login() {
|
||||
Don't have an account? <Link to="/register">Sign up here</Link>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Forgot Password Modal */}
|
||||
<Modal
|
||||
|
||||
@ -1,291 +1,125 @@
|
||||
.messages-page {
|
||||
min-height: 100vh;
|
||||
background-color: #f5f5f5;
|
||||
padding: 2rem 0;
|
||||
padding-top: 1.2rem;
|
||||
}
|
||||
|
||||
.messages-container {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
padding: 0 1rem;
|
||||
border: 1px solid rgba(212, 175, 55, 0.2);
|
||||
background: linear-gradient(135deg, rgba(18, 18, 18, 0.95), rgba(9, 9, 9, 0.95));
|
||||
border-radius: 16px;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
|
||||
.no-messages h3 {
|
||||
font-size: 1.5rem;
|
||||
color: #333;
|
||||
margin-bottom: 0.5rem;
|
||||
.messages-header h1 {
|
||||
font-size: 2rem;
|
||||
}
|
||||
|
||||
.no-messages p {
|
||||
color: #666;
|
||||
margin-bottom: 1.5rem;
|
||||
.messages-header p {
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
.messages-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1.5rem;
|
||||
display: grid;
|
||||
gap: 0.8rem;
|
||||
}
|
||||
|
||||
.message-card {
|
||||
background: white;
|
||||
border: 1px solid rgba(212, 175, 55, 0.18);
|
||||
border-radius: 12px;
|
||||
padding: 1.5rem;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||
transition: box-shadow 0.3s ease;
|
||||
}
|
||||
|
||||
.message-card:hover {
|
||||
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.15);
|
||||
background: rgba(255, 255, 255, 0.02);
|
||||
padding: 0.95rem;
|
||||
}
|
||||
|
||||
.message-card-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: flex-start;
|
||||
margin-bottom: 1.5rem;
|
||||
padding-bottom: 1rem;
|
||||
border-bottom: 1px solid #e5e5e5;
|
||||
}
|
||||
|
||||
.message-title-section {
|
||||
flex: 1;
|
||||
gap: 0.6rem;
|
||||
margin-bottom: 0.7rem;
|
||||
}
|
||||
|
||||
.message-title-section h3 {
|
||||
font-size: 1.4rem;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
margin: 0 0 0.5rem 0;
|
||||
margin-bottom: 0.2rem;
|
||||
}
|
||||
|
||||
.message-date {
|
||||
font-size: 0.9rem;
|
||||
color: #888;
|
||||
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;
|
||||
color: var(--text-soft);
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
.message-content h4,
|
||||
.admin-response h4 {
|
||||
font-size: 1rem;
|
||||
font-weight: 600;
|
||||
color: #10b981;
|
||||
margin-bottom: 0.75rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 0.35rem;
|
||||
color: var(--gold-bright);
|
||||
}
|
||||
|
||||
.response-icon {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
background: #10b981;
|
||||
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,
|
||||
.response-box,
|
||||
.message-details {
|
||||
border: 1px solid rgba(212, 175, 55, 0.15);
|
||||
background: rgba(0, 0, 0, 0.35);
|
||||
border-radius: 10px;
|
||||
padding: 0.75rem;
|
||||
}
|
||||
|
||||
.message-box p,
|
||||
.response-box p {
|
||||
margin: 0;
|
||||
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;
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
.waiting-response p {
|
||||
color: #888;
|
||||
color: var(--text-soft);
|
||||
font-style: italic;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.message-details-section {
|
||||
margin-top: 1.5rem;
|
||||
padding-top: 1.5rem;
|
||||
border-top: 1px solid #e5e5e5;
|
||||
}
|
||||
|
||||
.details-toggle {
|
||||
background: none;
|
||||
border: none;
|
||||
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;
|
||||
color: var(--gold-bright);
|
||||
margin-top: 0.75rem;
|
||||
}
|
||||
|
||||
.details-grid {
|
||||
margin-top: 0.6rem;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.detail-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.25rem;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.detail-item strong {
|
||||
font-weight: 600;
|
||||
color: #555;
|
||||
font-size: 0.9rem;
|
||||
color: var(--text-main);
|
||||
font-size: 0.86rem;
|
||||
}
|
||||
|
||||
.detail-item span {
|
||||
color: #666;
|
||||
font-size: 0.95rem;
|
||||
color: var(--text-muted);
|
||||
font-size: 0.86rem;
|
||||
}
|
||||
|
||||
.messages-actions {
|
||||
margin-top: 2rem;
|
||||
margin-top: 0.8rem;
|
||||
}
|
||||
|
||||
.no-messages {
|
||||
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 {
|
||||
background-color: #6c757d;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.btn-secondary:hover {
|
||||
background-color: #5a6268;
|
||||
}
|
||||
|
||||
/* 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 {
|
||||
.no-messages-icon {
|
||||
font-size: 2rem;
|
||||
margin-bottom: 0.4rem;
|
||||
}
|
||||
|
||||
@media (max-width: 720px) {
|
||||
.details-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.message-card-header {
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.status-badge {
|
||||
align-self: flex-start;
|
||||
}
|
||||
|
||||
.details-grid {
|
||||
grid-template-columns: 1fr;
|
||||
align-items: flex-start;
|
||||
}
|
||||
}
|
||||
|
||||
@ -137,7 +137,7 @@ export default function ProductDetail() {
|
||||
<p className="brand">{product.brand}</p>
|
||||
|
||||
<div className="rating">
|
||||
<span>⭐⭐⭐⭐⭐ (42 reviews)</span>
|
||||
<span>★★★★★ (42 reviews)</span>
|
||||
</div>
|
||||
|
||||
<div className="price">
|
||||
@ -151,6 +151,8 @@ export default function ProductDetail() {
|
||||
)}
|
||||
</div>
|
||||
|
||||
<p className="model-code">Model Code: {product.sku || product.id}</p>
|
||||
|
||||
<p className="description">{product.description}</p>
|
||||
|
||||
{product.sizes.length > 0 && (
|
||||
@ -198,10 +200,11 @@ export default function ProductDetail() {
|
||||
Add to Cart
|
||||
</button>
|
||||
<button
|
||||
className={`btn btn-icon ${inWishlist ? 'active' : ''}`}
|
||||
className={`btn-icon ${inWishlist ? 'active' : ''}`}
|
||||
onClick={handleToggleWishlist}
|
||||
aria-label="Add to wishlist"
|
||||
>
|
||||
{inWishlist ? '❤️' : '🤍'}
|
||||
{inWishlist ? '♥' : '♡'}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
|
||||
@ -110,7 +110,7 @@ export default function Products() {
|
||||
</span>
|
||||
<button
|
||||
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'}
|
||||
</button>
|
||||
@ -128,11 +128,11 @@ export default function Products() {
|
||||
|
||||
{/* Pagination controls (hidden when Show All is active) */}
|
||||
{!showAll && totalPages > 1 && (
|
||||
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', gap: '0.5rem', marginTop: '2rem', flexWrap: 'wrap' }}>
|
||||
<div className="pagination">
|
||||
<button
|
||||
onClick={() => setCurrentPage(p => Math.max(1, p - 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
|
||||
</button>
|
||||
@ -143,7 +143,7 @@ export default function Products() {
|
||||
<button
|
||||
key={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}
|
||||
</button>
|
||||
@ -152,7 +152,7 @@ export default function Products() {
|
||||
<button
|
||||
onClick={() => setCurrentPage(p => Math.min(totalPages, p + 1))}
|
||||
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 →
|
||||
</button>
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
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 { AuthContext } from '../context/AuthContext'
|
||||
import Toast from '../components/Toast'
|
||||
@ -26,6 +26,12 @@ export default function Profile() {
|
||||
const [saving, setSaving] = useState(false)
|
||||
const [changingPassword, setChangingPassword] = useState(false)
|
||||
const [toast, setToast] = useState(null)
|
||||
const [activeSection, setActiveSection] = useState('overview')
|
||||
const [stats, setStats] = useState({
|
||||
orders: 0,
|
||||
wishlist: 0,
|
||||
messages: 0,
|
||||
})
|
||||
|
||||
useEffect(() => {
|
||||
if (!token) {
|
||||
@ -37,9 +43,20 @@ export default function Profile() {
|
||||
|
||||
const fetchProfile = async () => {
|
||||
try {
|
||||
const response = await api.get('/users/me')
|
||||
setFormData(response.data)
|
||||
setUser(response.data)
|
||||
const [profileRes, ordersRes, wishlistRes, messagesRes] = await Promise.all([
|
||||
api.get('/users/me'),
|
||||
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) {
|
||||
console.error('Error fetching profile:', error)
|
||||
} finally {
|
||||
@ -103,25 +120,57 @@ export default function Profile() {
|
||||
|
||||
return (
|
||||
<div className="profile-page">
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: '1rem', marginBottom: '1rem' }}>
|
||||
<h1>My Profile</h1>
|
||||
{user?.is_admin && (
|
||||
<span style={{
|
||||
backgroundColor: '#ff6b6b',
|
||||
color: 'white',
|
||||
padding: '0.25rem 0.75rem',
|
||||
borderRadius: '4px',
|
||||
fontSize: '0.875rem',
|
||||
fontWeight: 'bold'
|
||||
}}>
|
||||
ADMIN
|
||||
</span>
|
||||
)}
|
||||
<h1>
|
||||
My Profile
|
||||
{user?.is_admin && <span className="admin-pill">ADMIN</span>}
|
||||
</h1>
|
||||
|
||||
<div className="profile-shell">
|
||||
<nav className="profile-nav">
|
||||
<button
|
||||
className={activeSection === 'overview' ? 'active' : ''}
|
||||
onClick={() => setActiveSection('overview')}
|
||||
type="button"
|
||||
>
|
||||
Overview
|
||||
</button>
|
||||
<Link to="/orders">Orders</Link>
|
||||
<Link to="/wishlist">Wishlist</Link>
|
||||
<Link to="/my-messages">Messages</Link>
|
||||
<button
|
||||
className={activeSection === 'addresses' ? 'active' : ''}
|
||||
onClick={() => setActiveSection('addresses')}
|
||||
type="button"
|
||||
>
|
||||
Addresses
|
||||
</button>
|
||||
<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>
|
||||
|
||||
<div className="profile-container">
|
||||
<form onSubmit={handleSubmit} className="profile-form">
|
||||
<h2>Personal Information</h2>
|
||||
<h2>{activeSection === 'addresses' ? 'Addresses' : 'Personal Information'}</h2>
|
||||
|
||||
<div className="form-group">
|
||||
<label>Full Name</label>
|
||||
@ -203,8 +252,8 @@ export default function Profile() {
|
||||
</button>
|
||||
</form>
|
||||
|
||||
<form onSubmit={handlePasswordChange} className="profile-form" style={{ marginTop: '2rem' }}>
|
||||
<h2>Change Password</h2>
|
||||
<form onSubmit={handlePasswordChange} className="profile-form">
|
||||
<h2>Account Settings</h2>
|
||||
|
||||
<div className="form-group">
|
||||
<label>Current Password</label>
|
||||
@ -243,6 +292,7 @@ export default function Profile() {
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{toast && (
|
||||
<Toast
|
||||
|
||||
@ -60,6 +60,18 @@ export default function Register() {
|
||||
return (
|
||||
<div className="auth-page">
|
||||
<div className="auth-container">
|
||||
<div className="auth-visual">
|
||||
<img
|
||||
src="https://images.unsplash.com/photo-1515955656352-a1fa3ffcd111?auto=format&fit=crop&w=1200&q=80"
|
||||
alt="Luxury sneaker"
|
||||
/>
|
||||
<div className="auth-visual-copy">
|
||||
<h3>Create Account</h3>
|
||||
<p>Join Brand Master and access exclusive collections and member-only drops.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="auth-panel">
|
||||
<h1>Create Account</h1>
|
||||
|
||||
<form onSubmit={handleSubmit}>
|
||||
@ -128,6 +140,7 @@ export default function Register() {
|
||||
Already have an account? <Link to="/login">Login here</Link>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{toast && (
|
||||
<Toast
|
||||
|
||||
@ -30,8 +30,8 @@ export default function Sales() {
|
||||
return (
|
||||
<div className="sales-page">
|
||||
<div className="sales-header">
|
||||
<h1>🔥 Limited Time Offers</h1>
|
||||
<p>Huge discounts on selected items - Limited time only!</p>
|
||||
<h1>Limited Time Offers</h1>
|
||||
<p>Luxury selections with exclusive markdowns. Ends soon.</p>
|
||||
</div>
|
||||
|
||||
{loading ? (
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user