diff --git a/src/controllers/accountController.js b/src/controllers/accountController.js index da6ad58..e10b2a8 100644 --- a/src/controllers/accountController.js +++ b/src/controllers/accountController.js @@ -11,7 +11,7 @@ const getUserAccount = asyncHandler(async (req, res) => { title: 'My Audify Account', header: req.session.user ? 'partials/login_header' : 'partials/header', viewName: 'users/userAccount', - activePage: 'Home', + activePage: 'account', isAdmin: false, user, }); @@ -51,7 +51,7 @@ const getAddresses = asyncHandler(async (req, res) => { title: 'Manage Address', header: req.session.user ? 'partials/login_header' : 'partials/header', viewName: 'users/manageAddress', - activePage: 'Home', + activePage: 'account', isAdmin: false, addresses, }); @@ -71,7 +71,8 @@ const getAddressDetails = asyncHandler(async (req, res) => { const addAddress = asyncHandler(async (req, res) => { const userId = req.session.user; await accountService.createAddress(userId, req.body); - res.redirect('/account/addresses'); + const redirectTo = req.body.redirectTo || '/account/addresses'; + res.redirect(redirectTo); }); const updateDefaultAddress = asyncHandler(async (req, res) => { @@ -105,7 +106,7 @@ const editAddressPage = asyncHandler(async (req, res) => { title: 'Edit Address', header: req.session.user ? 'partials/login_header' : 'partials/header', viewName: 'users/editAddress', - activePage: 'Home', + activePage: 'account', isAdmin: false, address, }); @@ -144,7 +145,7 @@ const walletTransactions = asyncHandler(async (req, res) => { title: 'My Audify Account', header: req.session.user ? 'partials/login_header' : 'partials/header', viewName: 'users/walletTransaction', - activePage: 'Home', + activePage: 'account', isAdmin: false, user, }); diff --git a/src/controllers/cartController.js b/src/controllers/cartController.js index f1af26b..3213f5d 100644 --- a/src/controllers/cartController.js +++ b/src/controllers/cartController.js @@ -11,7 +11,7 @@ const getCart = asyncHandler(async (req, res) => { title: 'Cart', header: req.session.user ? 'partials/login_header' : 'partials/header', viewName: 'users/cart', - activePage: 'Shop', + activePage: 'shop', isAdmin: false, cart, }); diff --git a/src/controllers/checkoutController.js b/src/controllers/checkoutController.js index b6610f3..497b382 100644 --- a/src/controllers/checkoutController.js +++ b/src/controllers/checkoutController.js @@ -23,7 +23,7 @@ const getCheckoutPage = asyncHandler(async (req, res) => { title: 'Checkout', header: 'partials/login_header', viewName: 'users/checkout', - activePage: 'Shop', + activePage: 'checkout', isAdmin: false, cart, addresses, @@ -128,7 +128,7 @@ const getPaymentPage = asyncHandler(async (req, res) => { title: 'Payment', header: 'partials/login_header', viewName: 'users/payment', - activePage: 'Shop', + activePage: 'checkout', isAdmin: false, cart, walletBalance, diff --git a/src/controllers/orderController.js b/src/controllers/orderController.js index 04bafab..f479594 100644 --- a/src/controllers/orderController.js +++ b/src/controllers/orderController.js @@ -20,7 +20,7 @@ const getOrderSuccessPage = asyncHandler(async (req, res) => { title: 'Order Success', header: 'partials/login_header', viewName: 'users/orderSuccess', - activePage: 'Order', + activePage: 'checkout', isAdmin: false, order, }); @@ -50,7 +50,7 @@ const getOrderHistory = asyncHandler(async (req, res) => { title: 'Order History', header: 'partials/login_header', viewName: 'users/orderHistory', - activePage: 'Order History', + activePage: 'account', isAdmin: false, orders, }); @@ -66,7 +66,7 @@ const getOrderDetail = asyncHandler(async (req, res) => { title: 'Order Detail', header: 'partials/login_header', viewName: 'users/orderDetail', - activePage: 'Order', + activePage: 'account', isAdmin: false, order, }); diff --git a/src/controllers/shopController.js b/src/controllers/shopController.js index 08d87d6..e327b55 100644 --- a/src/controllers/shopController.js +++ b/src/controllers/shopController.js @@ -197,7 +197,7 @@ const getWishList = asyncHandler(async (req, res) => { title: 'Wishlist', header: req.session.user ? 'partials/login_header' : 'partials/header', viewName: 'users/wishlist', - activePage: 'Shop', + activePage: 'shop', isAdmin: false, wishlist, }); diff --git a/src/public/css/error-pages.css b/src/public/css/error-pages.css index 4de6328..156fd4f 100644 --- a/src/public/css/error-pages.css +++ b/src/public/css/error-pages.css @@ -1,11 +1,12 @@ :root { - --error-bg: #090909; - --error-surface: #111111; - --error-border: #2a2a2a; - --error-text: #f0f0f0; - --error-muted: #a8a8a8; - --error-accent: #4f3b70; - --error-accent-hover: #5d4685; + --error-bg: #f8fafc; + --error-card-bg: #ffffff; + --error-primary: #2563eb; + --error-primary-hover: #1d4ed8; + --error-text: #0f172a; + --error-text-secondary: #64748b; + --error-border: #e2e8f0; + --error-radius: 24px; } *, @@ -17,130 +18,166 @@ body.error-page { margin: 0; min-height: 100vh; - font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; + font-family: + 'Inter', + system-ui, + -apple-system, + sans-serif; color: var(--error-text); - background: - radial-gradient( - circle at 15% 20%, - rgba(79, 59, 112, 0.14), - transparent 34% - ), - radial-gradient(circle at 85% 80%, rgba(79, 59, 112, 0.1), transparent 30%), - var(--error-bg); + background-color: var(--error-bg); + display: flex; + align-items: center; + justify-content: center; + overflow-x: hidden; } .error-shell { - min-height: 100vh; - display: grid; - place-items: center; - padding: 1rem; + width: 100%; + padding: 24px; + display: flex; + justify-content: center; } .error-card { - width: min(100%, 620px); - border: 1px solid var(--error-border); - border-radius: 18px; - background: linear-gradient(180deg, #121212, #0d0d0d); - box-shadow: 0 24px 50px rgba(0, 0, 0, 0.45); - padding: 1.5rem; + width: min(100%, 500px); + background: var(--error-card-bg); + border-radius: var(--error-radius); + padding: 60px 40px; + box-shadow: + 0 10px 25px -5px rgba(0, 0, 0, 0.05), + 0 8px 10px -6px rgba(0, 0, 0, 0.05); + text-align: center; + position: relative; + overflow: hidden; +} + +/* Decorative background circle */ +.error-card::before { + content: ''; + position: absolute; + top: -50px; + right: -50px; + width: 150px; + height: 150px; + background: rgba(37, 99, 235, 0.03); + border-radius: 50%; } .error-code { - margin: 0 0 0.4rem; - color: #cbbce5; - letter-spacing: 0.08em; - font-size: 0.78rem; - font-weight: 600; + font-size: 6rem; + font-weight: 800; + margin: 0; + background: linear-gradient(135deg, #2563eb, #6366f1); + -webkit-background-clip: text; + background-clip: text; + -webkit-text-fill-color: transparent; + line-height: 1; + letter-spacing: -2px; } .error-title { - margin: 0; - font-size: clamp(1.5rem, 3vw, 2.1rem); - font-weight: 650; - line-height: 1.2; + font-size: 1.75rem; + font-weight: 700; + margin: 16px 0 12px; + color: var(--error-text); } .error-description { - margin: 0.7rem 0 0; - color: var(--error-muted); + font-size: 1rem; + color: var(--error-text-secondary); line-height: 1.6; + margin: 0 auto 32px; + max-width: 320px; } .error-meta { - margin: 0.9rem 0 0; - color: #cdcdcd; - font-size: 0.92rem; - line-height: 1.5; + font-size: 0.875rem; + color: #ef4444; + background: #fef2f2; + padding: 8px 16px; + border-radius: 8px; + display: inline-block; + margin-bottom: 24px; } .error-details { - margin-top: 0.9rem; - border: 1px solid #2f2f2f; - border-radius: 10px; - background: #121212; - padding: 0.65rem 0.75rem; + text-align: left; + margin-bottom: 24px; } .error-details summary { - color: #d7d7d7; + font-size: 0.875rem; + color: var(--error-text-secondary); cursor: pointer; + padding: 8px 0; + font-weight: 500; +} + +.error-details summary:hover { + color: var(--error-primary); } .error-details pre { - margin: 0.65rem 0 0; - color: #c8c8c8; - font-size: 0.8rem; + margin-top: 12px; + padding: 16px; + background: #f1f5f9; + border-radius: 12px; + font-size: 0.75rem; + color: #475569; + max-height: 200px; + overflow-y: auto; white-space: pre-wrap; - word-break: break-word; - max-height: 220px; - overflow: auto; + word-break: break-all; } .error-actions { - margin-top: 1.1rem; display: flex; - gap: 0.6rem; - flex-wrap: wrap; + gap: 12px; + justify-content: center; } .error-btn { - border: 1px solid transparent; - border-radius: 11px; text-decoration: none; - min-height: 42px; - padding: 0.58rem 0.95rem; + padding: 12px 24px; + border-radius: 12px; + font-weight: 600; + font-size: 0.9375rem; + transition: all 0.2s ease; + border: none; + cursor: pointer; display: inline-flex; align-items: center; justify-content: center; - cursor: pointer; - font-size: 0.9rem; - font-weight: 600; } .error-btn-primary { - background: var(--error-accent); - color: #fff; + background-color: var(--error-primary); + color: #ffffff; } .error-btn-primary:hover { - background: var(--error-accent-hover); + background-color: var(--error-primary-hover); + transform: translateY(-1px); + box-shadow: 0 4px 12px rgba(37, 99, 235, 0.2); } .error-btn-secondary { - background: #191919; - color: #ececec; - border-color: #343434; + background-color: #f1f5f9; + color: #475569; } .error-btn-secondary:hover { - border-color: #4a4a4a; - color: #fff; + background-color: #e2e8f0; + color: #1e293b; } @media (max-width: 576px) { .error-card { - border-radius: 14px; - padding: 1.15rem; + padding: 40px 24px; + } + + .error-code { + font-size: 4.5rem; } .error-actions { diff --git a/src/public/css/filter.css b/src/public/css/filter.css deleted file mode 100644 index 8f586e7..0000000 --- a/src/public/css/filter.css +++ /dev/null @@ -1,25 +0,0 @@ -.btn-dark-purple { - background-color: #5a2b81; - color: white; -} - -.btn-dark-purple:hover { - background-color: #4b236d; - color: white; -} - -.btn-outline-dark-purple { - border-color: #5a2b81; - color: #5a2b81; -} - -.btn-outline-dark-purple:hover, -.btn-check:checked + .btn-outline-dark-purple { - background-color: #5a2b81; - color: white; -} - -.card-title { - font-weight: bold; - font-size: 1.25rem; -} diff --git a/src/public/css/footer.css b/src/public/css/footer.css deleted file mode 100644 index 9b51661..0000000 --- a/src/public/css/footer.css +++ /dev/null @@ -1,3 +0,0 @@ -footer { - background: #030013 !important; -} diff --git a/src/public/css/signup.css b/src/public/css/signup.css deleted file mode 100644 index 51557d1..0000000 --- a/src/public/css/signup.css +++ /dev/null @@ -1,14 +0,0 @@ -.img-container { - object-fit: cover; - overflow: hidden; -} -.signup-image { - max-width: 100%; - height: 100%; -} -.form-container { - padding: 20px; -} -.form-group label { - font-weight: bold; -} diff --git a/src/public/css/styles.css b/src/public/css/styles.css index 16b4601..bd496ae 100644 --- a/src/public/css/styles.css +++ b/src/public/css/styles.css @@ -1,7 +1,3 @@ -@import './footer.css'; -@import './signup.css'; -@import './filter.css'; - a { text-decoration: none; } @@ -79,28 +75,3 @@ a { .card-footer { padding: 16px; } - -.modal-content { - border-radius: 10px; - box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); -} - -.modal-header { - background-color: #f8f9fa; - border-bottom: 1px solid #dee2e6; - border-top-left-radius: 10px; - border-top-right-radius: 10px; -} - -.modal-title { - font-size: 1.25rem; - font-weight: bold; -} - -.modal-body { - padding: 2rem; -} - -.form-label { - font-weight: 500; -} diff --git a/src/public/css/user-account.css b/src/public/css/user-account.css deleted file mode 100644 index 3d2694c..0000000 --- a/src/public/css/user-account.css +++ /dev/null @@ -1,63 +0,0 @@ -body { - background: linear-gradient(to right, #f3f9fe, #faf3fe); -} -h2 { - color: #5a2d91; -} -.user-details { - background-color: rgba(255, 255, 255, 0.5); - border-radius: 8px; - padding: 30px; - box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); -} -.user-details label { - color: #072c69; /* Dark purple for labels */ - font-weight: bold; - margin-bottom: 5px; -} -.user-details .form-control { - color: #072c69; - background: rgba(255, 255, 255, 0.8); -} -.user-details .form-control:focus { - border-color: #5a2d91; /* Slightly darker purple for focus state */ - box-shadow: 0 0 0 0.2rem rgba(90, 45, 145, 0.25); /* Subtle shadow for focus */ -} -.account-buttons .btn { - font-size: 18px; - padding: 15px; - border-radius: 15px; - transition: all 0.3s ease; -} -.btn-primary { - background-color: #5a2d91; - border-color: #5a2d91; -} -.btn-primary:hover { - background-color: #6f42c1; - border-color: #6f42c1; - box-shadow: 0 0 5px rgba(0, 0, 0, 0.3); -} -.btn-secondary { - background-color: transparent; - border-color: #6f42c1; - border: 2px solid; - color: #6f42c1; -} -.btn-secondary:hover { - background-color: #6f42c1; - border-color: #6f42c1; - box-shadow: 0 0 5px rgba(0, 0, 0, 0.3); -} -.btn-group { - max-width: 800px; - margin: 0 auto; -} -.btn-link { - font-size: 20px; -} -.more { - background-color: rgba(255, 255, 255, 0.5); - box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); - border-radius: 8px; -} diff --git a/src/public/css/user-ui/account.css b/src/public/css/user-ui/account.css new file mode 100644 index 0000000..2c8f724 --- /dev/null +++ b/src/public/css/user-ui/account.css @@ -0,0 +1,828 @@ +/* =========================== + User Account Styles + =========================== */ + +.bg-blue-50 { + background-color: #eff6ff; +} +.text-blue-700 { + color: #1d4ed8; +} +.border-blue-100 { + border-color: #dbeafe !important; +} + +.account-container { + padding: 40px 0; +} + +.account-title { + font-size: 2rem; + font-weight: 700; + margin-bottom: 32px; + color: var(--text-color); +} + +/* ── Account Grid ── */ +.account-grid { + display: grid; + grid-template-columns: 280px 1fr; + gap: 32px; + align-items: start; +} + +/* ── Sidebar Navigation ── */ +.account-sidebar { + background: white; + border-radius: 24px; + padding: 24px; + box-shadow: 0 4px 20px rgba(0, 0, 0, 0.04); + border: 1px solid rgba(0, 0, 0, 0.02); +} + +.user-profile-brief { + text-align: center; + margin-bottom: 32px; + padding-bottom: 24px; + border-bottom: 1px solid #f1f5f9; +} + +.avatar-initial { + width: 80px; + height: 80px; + background: var(--primary-color); + color: white; + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + font-size: 1.75rem; + font-weight: 700; + margin: 0 auto 16px; + box-shadow: 0 8px 16px rgba(37, 99, 235, 0.2); +} + +.transaction-icon { + width: 48px; + height: 48px; + font-size: 1rem; + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + box-shadow: none !important; +} + +.transaction-icon.is-credit { + background: #dcfce7; + color: #16a34a; +} + +.transaction-icon.is-debit { + background: #eff6ff; + color: #2563eb; +} + +.user-profile-brief h5 { + font-weight: 700; + margin-bottom: 4px; +} + +.user-profile-brief p { + font-size: 0.85rem; + color: var(--text-secondary); + word-break: break-all; +} + +.account-nav-list { + list-style: none; + padding: 0; + margin: 0; +} + +.account-nav-item a { + display: flex; + align-items: center; + gap: 12px; + padding: 12px 16px; + border-radius: 12px; + color: var(--text-secondary); + text-decoration: none; + font-weight: 500; + transition: var(--transition); + margin-bottom: 4px; +} + +.account-nav-item a:hover { + background: #f8fafc; + color: var(--primary-color); +} + +.account-nav-item.active a { + background: #eff6ff; + color: var(--primary-color); +} + +/* ── Section Cards ── */ +.account-section-card { + background: white; + border-radius: 24px; + padding: 32px; + box-shadow: 0 4px 20px rgba(0, 0, 0, 0.04); + border: 1px solid rgba(0, 0, 0, 0.02); + margin-bottom: 32px; +} + +.section-header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 24px; +} + +.section-header h4 { + font-weight: 700; + margin-bottom: 0; +} + +/* ── Wallet Card ── */ +.wallet-card-modern { + background: linear-gradient(135deg, var(--primary-color) 0%, #1e40af 100%); + border-radius: 20px; + padding: 32px; + color: white; + position: relative; + overflow: hidden; + margin-bottom: 32px; +} + +.wallet-card-modern::before { + content: ''; + position: absolute; + top: -50px; + right: -50px; + width: 150px; + height: 150px; + background: rgba(255, 255, 255, 0.1); + border-radius: 50%; +} + +.wallet-label { + font-size: 0.9rem; + opacity: 0.8; + text-transform: uppercase; + letter-spacing: 0.05em; + margin-bottom: 8px; +} + +.wallet-amount { + font-size: 2.25rem; + font-weight: 800; + margin-bottom: 24px; +} + +.wallet-actions { + display: flex; + gap: 12px; +} + +.btn-wallet-action { + background: rgba(255, 255, 255, 0.2); + border: 1px solid rgba(255, 255, 255, 0.3); + color: white; + padding: 8px 20px; + border-radius: 10px; + font-size: 0.85rem; + font-weight: 600; + transition: var(--transition); +} + +.btn-wallet-action:hover { + background: white; + color: var(--primary-color); +} + +/* ── Quick Links ── */ +.quick-links-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); + gap: 20px; +} + +.quick-link-card { + background: #f8fafc; + border-radius: 16px; + padding: 24px; + text-decoration: none; + color: var(--text-color); + display: flex; + flex-direction: column; + gap: 12px; + transition: var(--transition); + border: 1px solid #f1f5f9; +} + +.quick-link-card:hover { + background: white; + border-color: var(--primary-color); + box-shadow: 0 4px 12px rgba(37, 99, 235, 0.1); + transform: translateY(-2px); +} + +.quick-link-icon { + width: 48px; + height: 48px; + background: white; + border-radius: 12px; + display: flex; + align-items: center; + justify-content: center; + font-size: 1.25rem; + color: var(--primary-color); + box-shadow: 0 2px 6px rgba(0, 0, 0, 0.04); +} + +.quick-link-card h6 { + font-weight: 700; + margin: 0; +} + +.quick-link-card p { + font-size: 0.8rem; + color: var(--text-secondary); + margin: 0; +} + +/* ── List Menu ── */ +.account-menu-list { + list-style: none; + padding: 0; + margin: 0; +} + +.account-menu-item { + border-bottom: 1px solid #f1f5f9; +} + +.account-menu-item:last-child { + border-bottom: none; +} + +.account-menu-item a { + display: flex; + align-items: center; + justify-content: space-between; + padding: 16px 0; + color: var(--text-color); + text-decoration: none; + transition: var(--transition); +} + +.account-menu-item a:hover { + color: var(--primary-color); +} + +.account-menu-icon { + width: 36px; + color: var(--text-secondary); + font-size: 1rem; +} + +/* ── Breadcrumb Overrides ── */ +.breadcrumb-modern { + margin-bottom: 24px; +} + +/* ── Logout Button ── */ +.btn-logout-modern { + color: #ef4444; + border-color: #fee2e2; + background: #fff5f5; + font-weight: 600; + border-radius: 14px; +} + +.btn-logout-modern:hover { + background: #ef4444; + color: white; + border-color: #ef4444; +} + +/* ── Responsive ── */ +@media (max-width: 991px) { + .account-grid { + grid-template-columns: 1fr; + gap: 24px; + } + + .account-sidebar { + display: none; /* Hide sidebar on mobile, maybe use a dropdown or just stack items */ + } +} + +@media (max-width: 768px) { + .account-container { + padding: 24px 0; + } + + .account-section-card { + padding: 24px; + } + + .wallet-card-modern { + padding: 24px; + } + + .wallet-amount { + font-size: 1.75rem; + } +} + +/* ── Address Cards ── */ +.address-grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); + gap: 24px; + margin-top: 24px; +} + +.address-card-modern { + background: white; + border: 1px solid #f1f5f9; + border-radius: 20px; + padding: 24px; + position: relative; + transition: var(--transition); + display: flex; + flex-direction: column; + justify-content: space-between; +} + +.address-card-modern:hover { + border-color: var(--primary-color); + box-shadow: 0 4px 12px rgba(37, 99, 235, 0.08); +} + +.address-card-modern.is-default { + border-color: var(--primary-color); + background: #f8fafc; +} + +.address-badge { + display: inline-block; + padding: 4px 12px; + border-radius: 8px; + font-size: 0.75rem; + font-weight: 700; + text-transform: uppercase; + background: var(--surface-color); + color: var(--text-secondary); + margin-bottom: 16px; +} + +.address-card-modern.is-default .address-badge { + background: var(--primary-color); + color: white; +} + +.address-details { + color: var(--text-color); + font-size: 0.95rem; + line-height: 1.6; + margin-bottom: 24px; +} + +.address-actions { + display: flex; + gap: 12px; + border-top: 1px solid #f1f5f9; + padding-top: 16px; +} + +.btn-address-action { + padding: 8px 16px; + font-size: 0.85rem; + font-weight: 600; + border-radius: 10px; + text-decoration: none; + transition: var(--transition); + display: flex; + align-items: center; + gap: 6px; +} + +.btn-address-edit { + background: #eff6ff; + color: var(--primary-color); +} + +.btn-address-delete { + background: #fff5f5; + color: #ef4444; +} + +.add-address-card-modern { + border: 2px dashed #e2e8f0; + background: transparent; + border-radius: 20px; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + gap: 12px; + padding: 40px; + cursor: pointer; + transition: var(--transition); + color: var(--text-secondary); + text-align: center; +} + +.add-address-card-modern:hover { + border-color: var(--primary-color); + color: var(--primary-color); + background: #f8fafc; +} + +.add-address-card-modern i { + font-size: 2rem; +} + +/* ── Empty State ── */ +.empty-state { + text-align: center; + padding: 40px 20px; +} + +.empty-state-icon { + width: 64px; + height: 64px; + background: #f1f5f9; + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + margin: 0 auto 16px; + color: #94a3b8; + font-size: 1.5rem; +} + +/* ── Order History ── */ +.order-list { + max-width: 1000px; + margin-top: 32px; +} + +.order-card-modern { + background: white; + border-radius: 20px; + padding: 24px; + margin-bottom: 20px; + border: 1px solid #f1f5f9; + transition: var(--transition); + cursor: pointer; + display: block; + text-decoration: none; + color: inherit; +} + +.order-card-modern:hover { + border-color: var(--primary-color); + box-shadow: 0 4px 15px rgba(37, 99, 235, 0.06); +} + +.order-header-row { + display: flex; + justify-content: space-between; + align-items: center; + flex-wrap: wrap; + gap: 12px; + margin-bottom: 20px; + padding-bottom: 16px; + border-bottom: 1px solid #f8fafc; +} + +.order-id { + font-weight: 700; + font-size: 0.95rem; + color: var(--text-color); + white-space: nowrap; +} + +.order-date { + font-size: 0.85rem; + color: var(--text-secondary); +} + +.order-content-row { + display: flex; + gap: 24px; + align-items: center; + flex-wrap: nowrap; +} + +.order-images-preview { + display: flex; + gap: 8px; + flex-shrink: 0; +} + +.order-img-sm { + width: 64px; + height: 64px; + border-radius: 14px; + background: var(--surface-color); + padding: 8px; + object-fit: contain; + border: 1px solid #f1f5f9; + flex-shrink: 0; +} + +.order-brief-info { + flex: 1; + min-width: 120px; +} + +.order-total-amount { + font-weight: 700; + font-size: 1.25rem; + color: var(--text-color); +} + +/* ── Status Pills ── */ +.status-pill { + padding: 6px 14px; + border-radius: 100px; + font-size: 0.72rem; + font-weight: 700; + display: inline-flex; + align-items: center; + gap: 6px; + text-transform: uppercase; + letter-spacing: 0.03em; +} + +.status-delivered { + background: #dcfce7; + color: #16a34a; +} +.status-shipped { + background: #e0f2fe; + color: #0284c7; +} +.status-cancelled { + background: #fee2e2; + color: #ef4444; +} +.status-processed { + background: #f3e8ff; + color: #9333ea; +} +.status-pending { + background: #fef3c7; + color: #d97706; +} +.status-requested { + background: #f1f5f9; + color: #64748b; + border: 1px dashed #cbd5e1; +} + +@media (max-width: 576px) { + .order-header-row { + flex-direction: column; + align-items: flex-start; + gap: 8px; + } + + .order-content-row { + flex-direction: column; + align-items: flex-start; + } + + .order-images-preview { + margin-bottom: 12px; + } +} + +/* ── Order Detail ── */ +.order-detail-header { + margin-bottom: 32px; +} + +.order-detail-grid { + display: grid; + grid-template-columns: 1fr 380px; + gap: 32px; + align-items: start; +} + +.order-tracking-card { + background: white; + border-radius: 24px; + padding: 32px; + margin-bottom: 32px; + border: 1px solid #f1f5f9; +} + +/* ── Stepper (Horizontal Tracking) ── */ +.tracking-stepper { + display: flex; + justify-content: space-between; + margin-top: 40px; + position: relative; + padding: 0 20px; +} + +.tracking-stepper::before { + content: ''; + position: absolute; + top: 14px; + left: 60px; + right: 60px; + height: 2px; + background: #f1f5f9; + z-index: 1; +} + +.tracking-stepper.stepper-cancelled::before { + display: none; +} + +.step-item { + position: relative; + z-index: 2; + text-align: center; + flex: 1; +} + +.step-dot { + width: 30px; + height: 30px; + background: white; + border: 2px solid #e2e8f0; + border-radius: 50%; + margin: 0 auto 12px; + display: flex; + align-items: center; + justify-content: center; + font-size: 0.75rem; + color: #94a3b8; + transition: var(--transition); +} + +.step-item.active .step-dot { + background: var(--primary-color); + border-color: var(--primary-color); + color: white; + box-shadow: 0 0 0 4px rgba(37, 99, 235, 0.15); +} + +.step-item.completed .step-dot { + background: #dcfce7; + border-color: #dcfce7; + color: #16a34a; +} + +.step-label { + font-size: 0.85rem; + font-weight: 600; + color: var(--text-secondary); +} + +.step-item.active .step-label { + color: var(--text-color); +} + +/* ── Info Cards Row ── */ +.info-cards-row { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 20px; + margin-bottom: 32px; +} + +.mini-info-card { + background: #f8fafc; + border-radius: 16px; + padding: 24px; + border: 1px solid #f1f5f9; +} + +.mini-info-card h6 { + font-weight: 700; + margin-bottom: 16px; + color: var(--text-color); + text-transform: uppercase; + font-size: 0.75rem; + letter-spacing: 0.05em; +} + +.info-text { + font-size: 0.9rem; + line-height: 1.6; + color: var(--text-color); + margin-bottom: 0; +} + +/* ── Item Detail Row ── */ +.order-item-detail { + display: flex; + gap: 16px; + padding: 16px 0; + border-bottom: 1px solid #f8fafc; +} + +.order-item-detail:last-child { + border-bottom: none; +} + +.order-item-detail img { + width: 80px; + height: 80px; + border-radius: 12px; + object-fit: contain; + background: var(--surface-color); + padding: 8px; +} + +/* ── Mobile Order Details ── */ +@media (max-width: 991px) { + .order-detail-grid { + grid-template-columns: 1fr; + } + + .info-cards-row { + grid-template-columns: 1fr; + } + + .order-summary-sidebar { + position: static; + margin-top: 32px; + } +} + +@media (max-width: 576px) { + .tracking-stepper { + flex-direction: column; + align-items: flex-start; + gap: 24px; + padding: 0; + } + + .tracking-stepper::before { + top: 0; + left: 14px; + width: 2px; + height: 100%; + } + + .step-item { + display: flex; + align-items: center; + gap: 16px; + text-align: left; + } + + .step-dot { + margin: 0; + } +} + +/* ── Order Summary Sidebar ── */ +.order-summary-sidebar { + position: sticky; + top: 100px; +} + +.summary-card { + background: white; + border-radius: 24px; + padding: 32px; + border: 1px solid #f1f5f9; +} + +.summary-title { + font-size: 1.25rem; + font-weight: 700; + margin-bottom: 24px; + color: var(--text-color); +} + +.summary-row { + display: flex; + justify-content: space-between; + margin-bottom: 16px; + font-size: 0.95rem; + color: var(--text-secondary); +} + +.summary-total { + display: flex; + justify-content: space-between; + font-weight: 800; + font-size: 1.5rem; + color: var(--text-color); + margin-top: 24px; + border-top: 2px solid #f8fafc; + padding-top: 24px; +} diff --git a/src/public/css/user-ui/auth.css b/src/public/css/user-ui/auth.css new file mode 100644 index 0000000..4234cb7 --- /dev/null +++ b/src/public/css/user-ui/auth.css @@ -0,0 +1,214 @@ +/* =========================== + Auth Pages — Minimal Centered + =========================== */ + +.auth-page { + min-height: 100vh; + display: flex; + align-items: center; + justify-content: center; + background-color: var(--surface-color); + padding: 40px 20px; +} + +.auth-card { + width: 100%; + max-width: 460px; + background: var(--bg-color); + border-radius: 24px; + padding: 48px 40px; + box-shadow: 0 4px 24px rgba(0, 0, 0, 0.06); +} + +@media (max-width: 576px) { + .auth-card { + padding: 32px 24px; + border-radius: 20px; + } +} + +/* Header */ +.auth-header { + text-align: center; + margin-bottom: 36px; +} + +.auth-logo { + height: 36px; + margin-bottom: 28px; +} + +.auth-title { + font-size: 1.75rem; + font-weight: 700; + margin-bottom: 8px; + color: var(--text-color); +} + +.auth-subtitle { + font-size: 0.95rem; + color: var(--text-secondary); + margin: 0; +} + +/* Social / OAuth */ +.auth-social { + display: flex; + gap: 12px; + margin-bottom: 24px; +} + +@media (max-width: 480px) { + .auth-social { + flex-direction: column; + } +} + +.btn-social { + flex: 1; + display: flex; + align-items: center; + justify-content: center; + gap: 10px; + padding: 11px 16px; + border: 1px solid #e2e8f0; + border-radius: 12px; + background: var(--bg-color); + font-size: 0.9rem; + font-weight: 500; + color: var(--text-color); + cursor: pointer; + transition: var(--transition); +} + +.btn-social:hover { + background: var(--surface-color); + border-color: #cbd5e1; +} + +.btn-social img { + width: 18px; + height: 18px; +} + +/* Divider */ +.auth-divider { + display: flex; + align-items: center; + gap: 16px; + margin-bottom: 24px; + font-size: 0.8rem; + font-weight: 500; + color: var(--text-secondary); + text-transform: uppercase; + letter-spacing: 0.05em; +} + +.auth-divider::before, +.auth-divider::after { + content: ''; + flex: 1; + height: 1px; + background-color: #e2e8f0; +} + +/* Form Fields */ +.auth-card .form-label { + font-size: 0.85rem; + font-weight: 500; + color: var(--text-color); + margin-bottom: 6px; +} + +.auth-card .form-control-modern { + border-radius: 12px; + padding: 11px 16px; + border: 1px solid #e2e8f0; + background-color: var(--bg-color); + font-size: 0.925rem; + transition: var(--transition); +} + +.auth-card .form-control-modern:focus { + border-color: var(--primary-color); + box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.08); + outline: none; +} + +.auth-card .form-control-modern::placeholder { + color: #94a3b8; +} + +/* Submit Button */ +.auth-card .btn-primary-modern { + border-radius: 12px; + padding: 12px; + font-size: 0.95rem; + font-weight: 600; +} + +/* Footer */ +.auth-footer { + margin-top: 28px; + text-align: center; + font-size: 0.9rem; + color: var(--text-secondary); +} + +.auth-footer a { + color: var(--primary-color); + font-weight: 600; + text-decoration: none; +} + +.auth-footer a:hover { + text-decoration: underline; +} + +/* ── OTP Verification ── */ +.otp-input-wrapper { + margin-bottom: 24px; +} + +.otp-input { + letter-spacing: 0.5rem; + font-size: 1.5rem; + font-weight: 700; + text-align: center; + padding: 12px; +} + +.resend-timer { + font-size: 0.875rem; + color: var(--text-secondary); + margin-top: 16px; + text-align: center; +} + +#countdown { + font-weight: 700; + color: var(--primary-color); + min-width: 20px; + display: inline-block; +} + +.btn-resend-link { + color: var(--primary-color); + font-weight: 600; + text-decoration: none; + background: none; + border: none; + padding: 0; + font-size: 0.875rem; + cursor: pointer; +} + +.btn-resend-link:hover { + text-decoration: underline; +} + +.btn-resend-link:disabled { + color: #94a3b8; + cursor: not-allowed; + text-decoration: none; +} diff --git a/src/public/css/user-ui/cart-wishlist.css b/src/public/css/user-ui/cart-wishlist.css new file mode 100644 index 0000000..c4a8d7a --- /dev/null +++ b/src/public/css/user-ui/cart-wishlist.css @@ -0,0 +1,258 @@ +/* =========================== + Cart & Wishlist Styles + =========================== */ + +/* ── Container Layout ── */ +.shopping-container { + padding: 40px 0; +} + +.shopping-title { + font-size: 2rem; + font-weight: 700; + margin-bottom: 32px; +} + +/* ── Cart Item (Horizontal Card) ── */ +.cart-item-card { + background: white; + border-radius: 20px; + padding: 24px; + margin-bottom: 20px; + box-shadow: 0 2px 12px rgba(0, 0, 0, 0.04); + border: 1px solid rgba(0, 0, 0, 0.02); + transition: var(--transition); + display: flex; + align-items: center; + gap: 24px; +} + +.cart-item-card:hover { + box-shadow: 0 4px 20px rgba(0, 0, 0, 0.06); +} + +.cart-item-img { + width: 120px; + height: 120px; + background: var(--surface-color); + border-radius: 16px; + padding: 12px; + display: flex; + align-items: center; + justify-content: center; + flex-shrink: 0; +} + +.cart-item-img img { + max-width: 100%; + max-height: 100%; + object-fit: contain; +} + +.cart-item-info { + flex: 1; +} + +.cart-item-info h5 { + font-size: 1.1rem; + font-weight: 600; + margin-bottom: 8px; + color: var(--text-color); +} + +.cart-item-price { + font-size: 1rem; + font-weight: 700; + color: var(--primary-color); +} + +/* ── Quantity Control ── */ +.quantity-control { + display: flex; + align-items: center; + background: var(--surface-color); + border-radius: 12px; + padding: 4px; + width: fit-content; +} + +.qty-btn { + width: 32px; + height: 32px; + border: none; + background: white; + border-radius: 8px; + display: flex; + align-items: center; + justify-content: center; + color: var(--text-color); + transition: var(--transition); + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05); +} + +.qty-btn:hover { + background: var(--primary-color); + color: white; +} + +.qty-input { + width: 40px; + border: none; + background: transparent; + text-align: center; + font-weight: 600; + font-size: 0.9rem; +} + +/* ── Cart Summary Sidebar ── */ +.summary-card { + background: white; + border-radius: 24px; + padding: 32px; + box-shadow: 0 4px 25px rgba(0, 0, 0, 0.05); + border: 1px solid rgba(0, 0, 0, 0.02); + position: sticky; + top: 100px; +} + +.summary-title { + font-size: 1.25rem; + font-weight: 700; + margin-bottom: 24px; + border-bottom: 1px solid #f1f5f9; + padding-bottom: 16px; +} + +.summary-row { + display: flex; + justify-content: space-between; + margin-bottom: 16px; + color: var(--text-secondary); + font-size: 0.95rem; +} + +.summary-total { + display: flex; + justify-content: space-between; + margin-top: 24px; + padding-top: 24px; + border-top: 2px solid #f1f5f9; + font-weight: 700; + font-size: 1.25rem; + color: var(--text-color); +} + +.btn-checkout { + margin-top: 32px; + height: 56px; + font-size: 1rem; + font-weight: 600; + border-radius: 16px; +} + +/* ── Wishlist Card (Vertical like Shop) ── */ +/* Reuses .product-card from shop.css, but we'll add specific wishlist overrides if needed */ +.wishlist-actions { + display: flex; + gap: 8px; +} + +/* ── Empty State ── */ +.empty-state { + text-align: center; + padding: 80px 20px; + background: white; + border-radius: 24px; + box-shadow: 0 2px 12px rgba(0, 0, 0, 0.04); +} + +.empty-state-icon { + width: 80px; + height: 80px; + background: #eff6ff; + color: var(--primary-color); + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + font-size: 2rem; + margin: 0 auto 24px; +} + +/* ── Responsive ── */ +@media (max-width: 991px) { + .shopping-container { + padding: 24px 0; + } + + .shopping-title { + font-size: 1.5rem; + margin-bottom: 24px; + } +} + +@media (max-width: 768px) { + .cart-item-card { + flex-direction: row; /* Keep image and info side-by-side if possible, or stack better */ + flex-wrap: wrap; + padding: 16px; + gap: 16px; + } + + .cart-item-img { + width: 80px; + height: 80px; + padding: 8px; + } + + .cart-item-info { + width: calc(100% - 100px); + flex: none; + } + + .cart-item-info h5 { + font-size: 0.95rem; + white-space: normal; + display: -webkit-box; + -webkit-line-clamp: 2; + line-clamp: 2; + -webkit-box-orient: vertical; + overflow: hidden; + } + + .cart-item-info .d-flex.align-items-center.justify-content-between { + flex-direction: column; + align-items: flex-start !important; + gap: 12px; + } + + .quantity-control { + order: 2; + } + + .cart-item-info .text-end { + order: 1; + text-align: left !important; + } + + .cart-item-card > .ms-lg-3 { + margin-left: 0 !important; + width: 100%; + display: flex; + justify-content: flex-end; + border-top: 1px solid #f1f5f9; + padding-top: 12px; + margin-top: 4px; + } +} + +@media (max-width: 576px) { + .shopping-title { + font-size: 1.25rem; + } + + .btn-primary-modern { + padding: 8px 16px; + font-size: 0.85rem; + } +} diff --git a/src/public/css/user-ui/checkout.css b/src/public/css/user-ui/checkout.css new file mode 100644 index 0000000..52d7600 --- /dev/null +++ b/src/public/css/user-ui/checkout.css @@ -0,0 +1,404 @@ +/* =========================== + Checkout Page Styles + =========================== */ + +.checkout-container { + padding: 40px 0; +} + +.checkout-title { + font-size: 2rem; + font-weight: 700; + margin-bottom: 32px; +} + +/* ── Checkout Item Card ── */ +.checkout-item-card { + background: white; + border-radius: 20px; + padding: 20px; + margin-bottom: 16px; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.03); + border: 1px solid rgba(0, 0, 0, 0.02); + display: flex; + align-items: center; + gap: 20px; +} + +.checkout-item-img { + width: 80px; + height: 80px; + background: var(--surface-color); + border-radius: 12px; + padding: 8px; + display: flex; + align-items: center; + justify-content: center; + flex-shrink: 0; +} + +.checkout-item-img img { + max-width: 100%; + max-height: 100%; + object-fit: contain; +} + +.checkout-item-info { + flex: 1; +} + +.checkout-item-info h6 { + font-weight: 600; + margin-bottom: 4px; + color: var(--text-color); +} + +.checkout-item-price { + font-size: 0.9rem; + color: var(--text-secondary); +} + +/* ── Address Selection ── */ +.address-grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); + gap: 20px; + margin-bottom: 32px; +} + +.address-card-modern { + background: white; + border-radius: 16px; + padding: 20px; + border: 2px solid #e2e8f0; + cursor: pointer; + transition: all 0.3s ease; + position: relative; + display: flex; + flex-direction: column; + justify-content: space-between; +} + +.address-card-modern:hover { + border-color: #cbd5e1; + transform: translateY(-2px); + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05); +} + +.address-card-modern.active-address { + border-color: var(--primary-color); + background-color: #f0f7ff; +} + +.address-card-modern.active-address::after { + content: '\f058'; + font-family: 'Font Awesome 6 Free'; + font-weight: 900; + position: absolute; + top: 12px; + right: 12px; + color: var(--primary-color); + font-size: 1.25rem; +} + +.address-badge { + display: inline-flex; + align-items: center; + padding: 4px 12px; + background: #f1f5f9; + color: var(--text-secondary); + border-radius: 20px; + font-size: 0.75rem; + font-weight: 600; + margin-bottom: 12px; + text-transform: uppercase; + letter-spacing: 0.5px; +} + +.address-card-modern.is-default .address-badge { + background: #eff6ff; + color: var(--primary-color); +} + +.address-details { + font-size: 0.9rem; + line-height: 1.5; + color: var(--text-color); +} + +/* ── Add New Address Card ── */ +.add-address-card-modern { + border: 2px dashed #cbd5e1; + background: #f8fafc; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + padding: 30px; + height: 100%; + border-radius: 16px; + cursor: pointer; + transition: all 0.3s ease; + text-align: center; +} + +.add-address-card-modern:hover { + background: #f1f5f9; + border-color: var(--primary-color); + color: var(--primary-color); +} + +.add-icon { + font-size: 2rem; + margin-bottom: 12px; + color: #94a3b8; + transition: inherit; +} + +.add-address-card-modern:hover .add-icon { + color: var(--primary-color); + transform: scale(1.1); +} + +/* ── Order Summary ── */ +.order-summary-card { + background: white; + border-radius: 24px; + padding: 32px; + box-shadow: 0 4px 25px rgba(0, 0, 0, 0.05); + border: 1px solid rgba(0, 0, 0, 0.02); + position: sticky; + top: 100px; +} + +.summary-title { + font-size: 1.25rem; + font-weight: 700; + margin-bottom: 24px; +} + +.summary-row { + display: flex; + justify-content: space-between; + margin-bottom: 16px; + color: var(--text-secondary); + font-size: 0.95rem; +} + +.summary-row.total { + margin-top: 24px; + padding-top: 24px; + border-top: 2px solid #f1f5f9; + font-weight: 700; + font-size: 1.25rem; + color: var(--text-color); +} + +.savings-text { + color: #10b981; + font-weight: 600; +} + +/* ── Coupon Section ── */ +.coupon-input-group { + display: flex; + gap: 12px; + margin-bottom: 24px; +} + +.coupon-input { + border-radius: 12px !important; + border: 1px solid #e2e8f0; + padding: 12px 16px; +} + +.btn-apply-coupon { + border-radius: 12px !important; + padding: 0 24px; + font-weight: 600; +} + +/* ── Payment Logic ── */ +.payment-methods { + margin-bottom: 32px; +} + +.payment-method-card { + background: white; + border-radius: 20px; + padding: 24px; + border: 2px solid #e2e8f0; + cursor: pointer; + transition: all 0.3s ease; + margin-bottom: 16px; + display: flex !important; + align-items: center; + position: relative; +} + +.payment-method-card:hover { + border-color: #cbd5e1; + background-color: #f8fafc; +} + +.payment-method-card.active-payment { + border-color: var(--primary-color); + background-color: #f0f7ff; +} + +.payment-method-card .form-check-input { + width: 22px; + height: 22px; + margin-right: 0; + cursor: pointer; + border: 2px solid #cbd5e1; +} + +.payment-method-card .form-check-input:checked { + background-color: var(--primary-color); + border-color: var(--primary-color); +} + +.payment-icon { + width: 52px; + height: 52px; + border-radius: 14px; + display: flex; + align-items: center; + justify-content: center; + font-size: 1.5rem; + margin-right: 20px; + flex-shrink: 0; +} + +.payment-details { + flex: 1; +} + +.payment-details h6 { + margin-bottom: 2px; + font-weight: 700; + color: var(--text-color); +} + +.payment-details p { + margin-bottom: 0; + font-size: 0.85rem; + color: var(--text-secondary); +} + +.bg-light-blue { + background-color: #eff6ff; + color: #2563eb; +} +.bg-light-green { + background-color: #f0fdf4; + color: #16a34a; +} +.bg-light-purple { + background-color: #f5f3ff; + color: #7c3aed; +} + +/* ── Order Success ── */ +.success-container { + max-width: 600px; + margin: 60px auto; + text-align: center; +} + +.success-icon-wrap { + position: relative; + width: 100px; + height: 100px; + margin: 0 auto 32px; +} + +.success-icon-circle { + width: 100%; + height: 100%; + background: #f0fdf4; + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + font-size: 3rem; + color: #16a34a; + animation: scaleUp 0.5s cubic-bezier(0.175, 0.885, 0.32, 1.275) both; +} + +.confetti { + position: absolute; + width: 100%; + height: 100%; + top: 0; + left: 0; + pointer-events: none; +} + +@keyframes scaleUp { + 0% { + transform: scale(0); + opacity: 0; + } + 100% { + transform: scale(1); + opacity: 1; + } +} + +.recap-card { + background: white; + border-radius: 24px; + padding: 32px; + box-shadow: 0 4px 20px rgba(0, 0, 0, 0.04); + border: 1px solid rgba(0, 0, 0, 0.02); + margin-top: 40px; + text-align: left; +} + +.info-item { + display: flex; + justify-content: space-between; + padding: 12px 0; + border-bottom: 1px solid #f1f5f9; +} + +.info-item:last-child { + border-bottom: none; +} + +.info-label { + color: var(--text-secondary); + font-size: 0.9rem; +} + +.info-value { + font-weight: 600; + color: var(--text-color); + font-size: 0.9rem; +} + +/* ── Mobile Responsive ── */ +@media (max-width: 991px) { + .order-summary-card { + position: static; + margin-top: 32px; + } +} + +@media (max-width: 576px) { + .address-grid { + grid-template-columns: 1fr; + } + + .checkout-item-card { + padding: 16px; + gap: 12px; + } + + .checkout-item-img { + width: 60px; + height: 60px; + } +} diff --git a/src/public/css/user-ui/home.css b/src/public/css/user-ui/home.css new file mode 100644 index 0000000..179a077 --- /dev/null +++ b/src/public/css/user-ui/home.css @@ -0,0 +1,149 @@ +/* Home Hero Section */ +.hero-section { + padding: 100px 0; + background: white; +} + +.hero-title { + font-size: 3.5rem; + line-height: 1.1; + margin-bottom: 24px; +} + +.hero-subtitle { + font-size: 1.25rem; + color: var(--text-secondary); + margin-bottom: 40px; + max-width: 540px; +} + +/* Categories Section */ +.category-card { + background: white; + border-radius: 20px; + overflow: hidden; + border: none; + box-shadow: 0 2px 12px rgba(0, 0, 0, 0.04); + transition: var(--transition); + text-decoration: none; + display: block; +} + +.category-card:hover { + box-shadow: 0 8px 30px rgba(0, 0, 0, 0.08); + transform: translateY(-6px); +} + +.category-img-wrap { + background-color: var(--surface-color); + padding: 32px 24px; + display: flex; + align-items: center; + justify-content: center; + height: 220px; + overflow: hidden; +} + +.category-img-wrap img { + max-width: 100%; + max-height: 100%; + object-fit: contain; + transition: transform 0.4s ease; +} + +.category-card:hover .category-img-wrap img { + transform: scale(1.08); +} + +.category-content { + padding: 24px; + text-align: center; +} + +.category-name { + font-size: 1.1rem; + font-weight: 700; + margin-bottom: 8px; + color: var(--text-color); +} + +.category-link { + font-weight: 600; + font-size: 0.9rem; + color: var(--primary-color); + text-decoration: none; + display: inline-flex; + align-items: center; + gap: 6px; + transition: var(--transition); +} + +.category-link:hover { + gap: 10px; +} + +/* ===== Responsive ===== */ + +@media (max-width: 991px) { + .hero-section { + padding: 60px 0; + } + + .hero-title { + font-size: 2.5rem; + text-align: center; + } + + .hero-subtitle { + text-align: center; + margin-left: auto; + margin-right: auto; + } + + .hero-cta { + justify-content: center; + } +} + +@media (max-width: 575px) { + .hero-section { + padding: 40px 0; + } + + .hero-title { + font-size: 1.75rem; + margin-bottom: 16px; + } + + .hero-subtitle { + font-size: 0.95rem; + margin-bottom: 24px; + } + + .hero-cta .btn { + font-size: 0.9rem; + padding: 10px 20px; + } + + .section-padding { + padding: 48px 0; + } + + .section-title { + font-size: 1.5rem; + margin-bottom: 28px; + } + + .category-img-wrap { + height: 180px; + padding: 24px 20px; + } + + .category-content { + padding: 16px; + } + + .category-name { + font-size: 1rem; + } +} diff --git a/src/public/css/user-ui/main.css b/src/public/css/user-ui/main.css new file mode 100644 index 0000000..f5b8915 --- /dev/null +++ b/src/public/css/user-ui/main.css @@ -0,0 +1,278 @@ +@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap'); + +:root { + --bg-color: #ffffff; + --surface-color: #f8fafc; + --primary-color: #2563eb; + --primary-hover: #1d4ed8; + --text-color: #0f172a; + --text-secondary: #64748b; + --border-radius: 16px; + --shadow-sm: 0 1px 2px 0 rgb(0 0 0 / 0.05); + --shadow-md: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1); + --shadow-lg: + 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1); + --transition: all 0.3s ease; +} + +body { + font-family: 'Inter', sans-serif; + background-color: var(--bg-color); + color: var(--text-color); + line-height: 1.6; + min-height: 100vh; + display: flex; + flex-direction: column; + /* Set base font size for rem units */ + font-size: 16px; +} + +/* ── Responsive Typography ── */ +@media (max-width: 991px) { + html { + font-size: 15px; /* Scales down all rem units slightly */ + } +} + +@media (max-width: 576px) { + html { + font-size: 14px; /* Proper base for small mobile */ + } + + h1 { + font-size: 1.75rem !important; + } + h2 { + font-size: 1.5rem !important; + } + h3 { + font-size: 1.25rem !important; + } + h4 { + font-size: 1.15rem !important; + } +} + +h1, +h2, +h3, +h4, +h5, +h6 { + font-weight: 700; + color: var(--text-color); + line-height: 1.2; +} + +.page-content { + flex: 1; +} + +/* White & Light Gray layout */ +.bg-surface { + background-color: var(--surface-color); +} + +/* Rounded corners & Soft shadows */ +.card-modern { + background: white; + border-radius: var(--border-radius); + border: none; + box-shadow: var(--shadow-md); + transition: var(--transition); + overflow: hidden; +} + +.card-modern:hover { + box-shadow: var(--shadow-lg); + transform: translateY(-4px); +} + +/* Buttons */ +.btn-primary-modern { + background-color: var(--primary-color); + color: white; + border-radius: 12px; + padding: 10px 24px; + font-weight: 500; + border: none; + transition: var(--transition); + display: inline-flex; + align-items: center; + gap: 8px; +} + +.btn-primary-modern:hover { + background-color: var(--primary-hover); + color: white; + transform: translateY(-2px); + box-shadow: 0 4px 12px rgba(37, 99, 235, 0.2); +} + +.btn-secondary-modern { + background-color: white; + color: var(--text-color); + border: 1px solid #e2e8f0; + border-radius: 12px; + padding: 10px 24px; + font-weight: 500; + transition: var(--transition); +} + +.btn-secondary-modern:hover { + background-color: var(--surface-color); + border-color: #cbd5e1; +} + +/* Form Styles */ +.form-control-modern { + border-radius: 12px; + padding: 12px 16px; + border: 1px solid #e2e8f0; + background-color: var(--surface-color); + transition: var(--transition); +} + +.form-control-modern:focus { + border-color: var(--primary-color); + box-shadow: 0 0 0 4px rgba(37, 99, 235, 0.1); + outline: none; + background-color: white; +} + +/* Layout Utilities */ +.section-padding { + padding: 80px 0; +} + +.section-title { + font-size: 2.5rem; + margin-bottom: 48px; + text-align: center; +} + +/* Product Image Optimization */ +.product-img-large { + width: 100%; + height: auto; + object-fit: contain; + border-radius: var(--border-radius); +} + +/* Navbar Modern */ +.navbar-modern { + background: rgba(255, 255, 255, 0.95); + backdrop-filter: blur(12px); + -webkit-backdrop-filter: blur(12px); + border-bottom: 1px solid rgba(0, 0, 0, 0.05); + padding: 12px 0; +} + +.nav-link-modern { + font-weight: 500; + color: var(--text-secondary); + transition: var(--transition); +} + +.nav-link-modern:hover, +.nav-link-modern.active { + color: var(--primary-color); +} + +/* Nav icon links (wishlist, cart, account) */ +.nav-icon-link { + display: flex; + align-items: center; + justify-content: center; + width: 42px; + height: 42px; + border-radius: 50%; + background: var(--surface-color); + border: 1px solid rgba(0, 0, 0, 0.05); + transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); + color: var(--text-secondary); + text-decoration: none; +} + +.nav-icon-fa { + font-size: 1.1rem; + transition: inherit; +} + +.nav-icon-link:hover { + background-color: var(--primary-color); + color: white !important; + transform: translateY(-2px); + box-shadow: 0 4px 12px rgba(37, 99, 235, 0.2); + border-color: var(--primary-color); +} + +.nav-icon-link:hover .nav-icon-fa { + color: white !important; +} + +.profile-icon-link { + border: 1.5px solid #e2e8f0; + background: white; +} + +/* ===== Mobile Menu ===== */ +@media (max-width: 991px) { + .navbar-modern .navbar-collapse { + background: white; + border-radius: 20px; + margin-top: 12px; + padding: 20px; + box-shadow: 0 12px 40px rgba(0, 0, 0, 0.08); + border: 1px solid rgba(0, 0, 0, 0.04); + } + + /* Nav links — large tap targets */ + .mobile-nav-list { + gap: 0 !important; + padding-bottom: 16px; + margin-bottom: 16px; + border-bottom: 1px solid #f1f5f9; + } + + .mobile-nav-list .nav-link { + padding: 14px 16px !important; + border-radius: 12px; + font-size: 1rem; + } + + .mobile-nav-list .nav-link:hover { + background-color: var(--surface-color); + } + + .mobile-nav-list .nav-link.active { + background-color: #eff6ff; + color: var(--primary-color); + font-weight: 600; + } + + /* Icon row — centered with separator */ + .mobile-icon-row { + padding: 16px 0; + margin-bottom: 16px; + border-bottom: 1px solid #f1f5f9; + } + + .mobile-icon-row .nav-icon-link { + width: 48px; + height: 48px; + background-color: var(--surface-color); + border-radius: 50%; + } + + /* Auth buttons — full width */ + .mobile-auth-actions { + gap: 10px; + } + + .mobile-auth-actions .btn { + width: 100%; + padding: 14px; + font-size: 0.95rem; + } +} diff --git a/src/public/css/user-ui/product-detail.css b/src/public/css/user-ui/product-detail.css new file mode 100644 index 0000000..ec11aa7 --- /dev/null +++ b/src/public/css/user-ui/product-detail.css @@ -0,0 +1,268 @@ +/* =========================== + Product Detail Page + =========================== */ + +.product-detail-layout { + padding: 40px 0; +} + +/* ── Breadcrumb ── */ +.breadcrumb-modern { + margin-bottom: 32px; +} + +.breadcrumb-modern .breadcrumb-item { + font-size: 0.9rem; + font-weight: 500; +} + +.breadcrumb-modern .breadcrumb-item a { + color: var(--text-secondary); + text-decoration: none; + transition: var(--transition); +} + +.breadcrumb-modern .breadcrumb-item a:hover { + color: var(--primary-color); +} + +.breadcrumb-modern .breadcrumb-item.active { + color: var(--text-color); + font-weight: 600; +} + +/* ── Image Gallery ── */ +.product-gallery { + display: flex; + gap: 20px; +} + +.gallery-thumbs { + display: flex; + flex-direction: column; + gap: 12px; + width: 80px; + flex-shrink: 0; +} + +.thumb-item { + width: 80px; + height: 80px; + padding: 8px; + background: white; + border: 2px solid transparent; + border-radius: 12px; + cursor: pointer; + transition: var(--transition); + display: flex; + align-items: center; + justify-content: center; +} + +.thumb-item img { + max-width: 100%; + max-height: 100%; + object-fit: contain; +} + +.thumb-item:hover, +.thumb-item.active { + border-color: var(--primary-color); +} + +.gallery-main { + flex: 1; + background: white; + border-radius: 20px; + padding: 40px; + display: flex; + align-items: center; + justify-content: center; + position: relative; + overflow: hidden; + box-shadow: 0 4px 20px rgba(0, 0, 0, 0.03); +} + +.gallery-main img { + max-width: 100%; + max-height: 500px; + object-fit: contain; + transition: transform 0.1s ease-out; +} + +/* ── Product Info ── */ +.detail-info { + padding-left: 20px; +} + +.detail-name { + font-size: 2rem; + font-weight: 700; + color: var(--text-color); + margin-bottom: 12px; + line-height: 1.3; +} + +.detail-price-row { + display: flex; + align-items: baseline; + gap: 12px; + margin-bottom: 24px; +} + +.detail-price { + font-size: 1.75rem; + font-weight: 700; + color: var(--primary-color); +} + +.detail-price-original { + font-size: 1.1rem; + color: var(--text-secondary); + text-decoration: line-through; +} + +.detail-desc { + color: var(--text-secondary); + font-size: 1rem; + line-height: 1.7; + margin-bottom: 32px; +} + +/* ── Stock Badge ── */ +.stock-status { + display: inline-flex; + align-items: center; + gap: 8px; + padding: 6px 14px; + border-radius: 10px; + font-size: 0.9rem; + font-weight: 600; + margin-bottom: 32px; +} + +.stock-status.in-stock { + background-color: #dcfce7; + color: #166534; +} + +.stock-status.out-of-stock { + background-color: #fee2e2; + color: #991b1b; +} + +/* ── Actions ── */ +.detail-actions { + display: flex; + gap: 16px; + margin-bottom: 40px; +} + +.btn-detail-add-cart { + flex: 1; + height: 54px; + background: var(--primary-color); +} + +.btn-detail-wishlist { + width: 54px; + height: 54px; + display: flex; + align-items: center; + justify-content: center; + border: 1px solid #e2e8f0; + border-radius: 14px; + background: white; + color: var(--text-secondary); + transition: var(--transition); +} + +.btn-detail-wishlist:hover { + border-color: #ef4444; + color: #ef4444; + background: #fef2f2; +} + +/* ── Assurance Grid ── */ +.assurance-section { + padding: 40px 0; + border-top: 1px solid #f1f5f9; + border-bottom: 1px solid #f1f5f9; + margin: 60px 0; +} + +.assurance-item { + display: flex; + flex-direction: column; + align-items: center; + text-align: center; +} + +.assurance-icon { + width: 60px; + height: 60px; + background: #eff6ff; + border-radius: 16px; + display: flex; + align-items: center; + justify-content: center; + color: var(--primary-color); + font-size: 1.5rem; + margin-bottom: 16px; +} + +.assurance-text h6 { + font-size: 0.95rem; + font-weight: 700; + margin-bottom: 4px; +} + +.assurance-text p { + font-size: 0.85rem; + color: var(--text-secondary); + margin-bottom: 0; +} + +/* ── Magnifier ── */ +.zoom-hint { + position: absolute; + bottom: 20px; + right: 20px; + background: rgba(255, 255, 255, 0.8); + backdrop-filter: blur(4px); + padding: 8px 12px; + border-radius: 10px; + font-size: 0.75rem; + font-weight: 600; + color: var(--text-secondary); + pointer-events: none; +} + +/* ── Responsive ── */ +@media (max-width: 991px) { + .product-gallery { + flex-direction: column-reverse; + } + + .gallery-thumbs { + flex-direction: row; + width: 100%; + overflow-x: auto; + padding-bottom: 8px; + } + + .detail-info { + padding-left: 0; + margin-top: 40px; + } +} + +@media (max-width: 576px) { + .detail-name { + font-size: 1.5rem; + } + + .detail-price { + font-size: 1.5rem; + } +} diff --git a/src/public/css/user-ui/shop.css b/src/public/css/user-ui/shop.css new file mode 100644 index 0000000..2792704 --- /dev/null +++ b/src/public/css/user-ui/shop.css @@ -0,0 +1,432 @@ +/* =========================== + Shop Page — Modern Design + =========================== */ + +/* Shop Layout */ +.shop-layout { + padding: 32px 0; +} + +/* ── Filter Sidebar ── */ +.filter-panel { + background: white; + border-radius: 20px; + padding: 28px; + box-shadow: 0 2px 12px rgba(0, 0, 0, 0.04); + border: none; + position: sticky; + top: 90px; +} + +.filter-panel .filter-heading { + font-size: 1.1rem; + font-weight: 700; + margin-bottom: 24px; + display: flex; + justify-content: space-between; + align-items: center; +} + +.filter-panel .form-label { + font-size: 0.85rem; + font-weight: 600; + color: var(--text-secondary); + text-transform: uppercase; + letter-spacing: 0.04em; + margin-bottom: 8px; +} + +.filter-panel .form-select { + border-radius: 12px; + padding: 10px 14px; + border: 1px solid #e2e8f0; + font-size: 0.9rem; + background-color: var(--surface-color); + transition: var(--transition); +} + +.filter-panel .form-select:focus { + border-color: var(--primary-color); + box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.08); +} + +.btn-reset-filter { + font-size: 0.8rem; + font-weight: 600; + color: var(--text-secondary); + border: 1px solid #e2e8f0; + border-radius: 10px; + padding: 6px 14px; + background: white; + transition: var(--transition); +} + +.btn-reset-filter:hover { + color: var(--primary-color); + border-color: var(--primary-color); + background-color: #eff6ff; +} + +/* ── Search Bar ── */ +.shop-search { + background: white; + border-radius: 14px; + border: 1px solid #e2e8f0; + padding: 4px; + transition: var(--transition); +} + +.shop-search:focus-within { + border-color: var(--primary-color); + box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.08); +} + +.shop-search .input-group-text { + background: transparent; + border: none; + padding-left: 14px; +} + +.shop-search .form-control { + border: none; + box-shadow: none; + font-size: 0.9rem; + padding: 10px 12px; +} + +.shop-search .form-control:focus { + box-shadow: none; +} + +/* ── Product Cards ── */ +.product-card { + background: white; + border-radius: 20px; + overflow: hidden; + border: none; + box-shadow: 0 2px 12px rgba(0, 0, 0, 0.04); + transition: var(--transition); + display: flex; + flex-direction: column; +} + +.product-card:hover { + box-shadow: 0 8px 28px rgba(0, 0, 0, 0.08); + transform: translateY(-4px); +} + +.product-card .product-img-wrap { + background: var(--surface-color); + padding: 24px; + display: flex; + align-items: center; + justify-content: center; + height: 220px; + position: relative; + overflow: hidden; +} + +.product-card .product-img-wrap img { + max-width: 100%; + max-height: 100%; + object-fit: contain; + transition: transform 0.4s ease; +} + +.product-card:hover .product-img-wrap img { + transform: scale(1.06); +} + +/* Discount badge */ +.product-badge { + position: absolute; + top: 12px; + left: 12px; + background: #ef4444; + color: white; + font-size: 0.75rem; + font-weight: 700; + padding: 4px 10px; + border-radius: 8px; + z-index: 2; +} + +/* Product info */ +.product-info { + padding: 20px; + flex: 1; + display: flex; + flex-direction: column; +} + +.product-name { + font-size: 0.95rem; + font-weight: 600; + color: var(--text-color); + margin-bottom: 8px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.product-name a { + color: inherit; + text-decoration: none; +} + +.product-name a:hover { + color: var(--primary-color); +} + +.product-price { + font-size: 1.1rem; + font-weight: 700; + color: var(--text-color); + margin-bottom: 4px; +} + +.product-price-original { + font-size: 0.85rem; + color: var(--text-secondary); + text-decoration: line-through; + margin-left: 8px; + font-weight: 400; +} + +.product-stock { + font-size: 0.8rem; + font-weight: 500; +} + +.product-stock.in-stock { + color: #22c55e; +} + +.product-stock.out-of-stock { + color: #ef4444; +} + +/* Product actions */ +.product-actions { + padding: 0 20px 20px; + display: flex; + gap: 8px; +} + +.btn-add-cart { + flex: 1; + background: var(--primary-color); + color: white; + border: none; + border-radius: 12px; + padding: 10px 16px; + font-size: 0.85rem; + font-weight: 600; + transition: var(--transition); + display: flex; + align-items: center; + justify-content: center; + gap: 6px; +} + +.btn-add-cart:hover { + background: var(--primary-hover); + color: white; +} + +.btn-add-cart:disabled, +.btn-add-cart.disabled { + opacity: 0.4; + pointer-events: none; +} + +.btn-wishlist { + width: 42px; + height: 42px; + display: flex; + align-items: center; + justify-content: center; + border: 1px solid #e2e8f0; + border-radius: 12px; + background: white; + color: var(--text-secondary); + transition: var(--transition); + flex-shrink: 0; +} + +.btn-wishlist:hover { + border-color: #ef4444; + color: #ef4444; + background: #fef2f2; +} + +/* Out of stock card */ +.product-card.is-out-of-stock { + opacity: 0.55; +} + +.product-card.is-out-of-stock .btn-add-cart { + pointer-events: none; +} + +/* ── Empty State ── */ +.shop-empty-state { + text-align: center; + padding: 60px 20px; +} + +.shop-empty-state i { + font-size: 3rem; + color: #cbd5e1; + margin-bottom: 16px; +} + +.shop-empty-state p { + color: var(--text-secondary); + margin-bottom: 16px; +} + +/* ── Result Count ── */ +.result-count { + font-size: 0.85rem; + color: var(--text-secondary); + font-weight: 500; +} + +/* ── Slider Overrides ── */ +.filter-panel #price-slider .noUi-connect { + background: var(--primary-color); +} + +.filter-panel .noUi-horizontal .noUi-handle { + background-color: var(--primary-color); + border-radius: 50%; + width: 18px; + height: 18px; + border: none; + box-shadow: 0 2px 6px rgba(37, 99, 235, 0.3); +} + +.filter-panel .noUi-handle:before, +.filter-panel .noUi-handle:after { + display: none; +} + +/* ── Mobile Filter Toggle Button ── */ +.btn-filter-toggle { + display: flex; + align-items: center; + gap: 8px; + padding: 10px 20px; + background: white; + border: 1px solid #e2e8f0; + border-radius: 14px; + font-size: 0.9rem; + font-weight: 600; + color: var(--text-color); + white-space: nowrap; + cursor: pointer; + transition: var(--transition); +} + +.btn-filter-toggle:hover { + border-color: var(--primary-color); + color: var(--primary-color); +} + +.btn-filter-toggle i { + font-size: 1rem; +} + +/* ── Close Button (inside drawer) ── */ +.btn-close-filter { + width: 36px; + height: 36px; + display: flex; + align-items: center; + justify-content: center; + border: none; + background: var(--surface-color); + border-radius: 10px; + color: var(--text-secondary); + cursor: pointer; + transition: var(--transition); +} + +.btn-close-filter:hover { + background: #e2e8f0; + color: var(--text-color); +} + +/* ── Backdrop Overlay ── */ +.filter-backdrop { + display: none; + position: fixed; + inset: 0; + background: rgba(15, 23, 42, 0.3); + backdrop-filter: blur(4px); + -webkit-backdrop-filter: blur(4px); + z-index: 1040; + opacity: 0; + transition: opacity 0.3s ease; +} + +.filter-backdrop.is-visible { + display: block; + opacity: 1; +} + +/* ── Responsive ── */ +@media (max-width: 991px) { + /* Off-canvas drawer */ + .filter-col { + position: fixed; + top: 0; + left: 0; + bottom: 0; + width: 320px; + max-width: 85vw; + z-index: 1050; + padding: 0; + transform: translateX(-100%); + transition: transform 0.35s cubic-bezier(0.4, 0, 0.2, 1); + overflow-y: auto; + background: white; + } + + .filter-col.is-open { + transform: translateX(0); + } + + .filter-col .filter-panel { + border-radius: 0; + box-shadow: none; + position: static; + min-height: 100vh; + padding: 24px; + } +} + +@media (max-width: 767px) { + .shop-layout { + padding: 16px 0; + } + + .product-card .product-img-wrap { + height: 180px; + padding: 16px; + } + + .product-info { + padding: 16px; + } + + .product-actions { + padding: 0 16px 16px; + } +} + +/* Body scroll lock when drawer is open */ +body.filter-drawer-open { + overflow: hidden; +} diff --git a/src/public/css/user.css b/src/public/css/user.css deleted file mode 100644 index 3bbf20a..0000000 --- a/src/public/css/user.css +++ /dev/null @@ -1,51 +0,0 @@ -body { - background: linear-gradient(to right, #f3f9fe, #faf3fe); -} - -.order-status-pill { - display: inline-flex; - align-items: center; - gap: 0.28rem; - border-radius: 999px; - padding: 0.28rem 0.72rem; - font-size: 0.76rem; - font-weight: 600; - letter-spacing: 0.01em; - border: 1px solid transparent; -} - -.order-status-pill.is-delivered { - color: #1f6b4f; - background: #e7f6ee; - border-color: #c2e8d3; -} - -.order-status-pill.is-cancelled { - color: #9b3a4c; - background: #fdeef1; - border-color: #f7cfd8; -} - -.order-status-pill.is-shipped { - color: #285e97; - background: #e9f2fe; - border-color: #c8ddfb; -} - -.order-status-pill.is-processed { - color: #2c6a77; - background: #e7f6f8; - border-color: #c7e6ec; -} - -.order-status-pill.is-pending { - color: #8b5d16; - background: #fff6df; - border-color: #f5e2ac; -} - -.order-status-pill-requested { - color: #7f601e; - background: #fff4d6; - border-color: #f2dfab; -} diff --git a/src/public/css/wishlist.css b/src/public/css/wishlist.css deleted file mode 100644 index 823734f..0000000 --- a/src/public/css/wishlist.css +++ /dev/null @@ -1,64 +0,0 @@ -/* Modal Background */ -.modal { - display: none; /* Hidden by default */ - position: fixed; - right: 0; - top: 0; - width: 300px; - height: 100%; - background-color: #fff; - box-shadow: -2px 0px 10px rgba(0, 0, 0, 0.2); - z-index: 1000; - overflow-y: auto; - transition: transform 0.3s ease-in-out; - transform: translateX(100%); -} - -.modal-content { - padding: 20px; -} - -.close-btn { - position: absolute; - top: 10px; - right: 15px; - font-size: 24px; - cursor: pointer; -} - -.product-list { - display: flex; - flex-direction: column; -} - -.product-item { - display: flex; - margin-bottom: 20px; -} - -.product-image { - width: 80px; - height: 80px; - object-fit: cover; - margin-right: 10px; -} - -.product-info { - display: flex; - flex-direction: column; -} - -.product-name { - font-size: 16px; - margin: 0; -} - -.product-price { - font-size: 14px; - color: #555; -} - -/* Show the modal */ -.modal.show { - transform: translateX(0); -} diff --git a/src/public/images/banner.jpg b/src/public/images/banner.jpg deleted file mode 100644 index 33bf4c1..0000000 Binary files a/src/public/images/banner.jpg and /dev/null differ diff --git a/src/public/images/svg/Footer_logo.svg b/src/public/images/svg/Footer_logo.svg deleted file mode 100644 index 54b1319..0000000 --- a/src/public/images/svg/Footer_logo.svg +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - diff --git a/src/public/images/svg/audify-icon.svg b/src/public/images/svg/audify-icon.svg new file mode 100644 index 0000000..6449168 --- /dev/null +++ b/src/public/images/svg/audify-icon.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/src/public/images/svg/logo.svg b/src/public/images/svg/logo.svg index 04a7ffe..7b510dd 100644 --- a/src/public/images/svg/logo.svg +++ b/src/public/images/svg/logo.svg @@ -1,13 +1,13 @@ - - - - - + + + + + - + - - + + diff --git a/src/public/images/svg/logo_dark.svg b/src/public/images/svg/logo_dark.svg index d298573..2c99e21 100644 --- a/src/public/images/svg/logo_dark.svg +++ b/src/public/images/svg/logo_dark.svg @@ -1,13 +1,13 @@ - - - - - + + + + + - + - - + + diff --git a/src/public/js/addNewAddress.js b/src/public/js/addNewAddress.js index 0606c84..29101bc 100644 --- a/src/public/js/addNewAddress.js +++ b/src/public/js/addNewAddress.js @@ -1,65 +1,33 @@ +// Custom name field visibility document.querySelectorAll('input[name="addressType"]').forEach((radio) => { radio.addEventListener('change', () => { - document.getElementById('customNameContainer').style.display = - radio.id === 'custom' ? 'block' : 'none'; + const customContainer = document.getElementById('customNameContainer'); + if (customContainer) { + customContainer.style.display = + radio.value === 'other' ? 'block' : 'none'; + } }); }); +// Redirect to edit page document.addEventListener('DOMContentLoaded', () => { - document.querySelectorAll('.edit-icon').forEach((icon) => { - icon.addEventListener('click', (event) => { - event.stopPropagation(); - const addressId = event.target.closest('.address-card').dataset.id; - window.location.href = `/account/addresses/edit/${addressId}`; + document.querySelectorAll('.btn-address-edit').forEach((btn) => { + btn.addEventListener('click', (event) => { + // If it's a link, it will just follow the href. + // This is here as a fallback if needed. }); }); }); -document.getElementById('addressForm').addEventListener('submit', (event) => { - const checkbox = document.getElementById('defaultAddress'); - const hiddenInput = document.querySelector( - 'input[name="isDefault"][type="hidden"]' - ); +// Sync default checkbox with hidden input +const addressForm = document.getElementById('addressForm'); +if (addressForm) { + addressForm.addEventListener('submit', () => { + const checkbox = document.getElementById('isDefaultCheck'); + const hiddenInput = document.getElementById('isDefaultHidden'); - if (checkbox.checked) { - hiddenInput.value = true; - } else { - hiddenInput.value = false; - } -}); - -document.addEventListener('DOMContentLoaded', () => { - document.querySelectorAll('.delete-icon').forEach((icon) => { - icon.addEventListener('click', async (event) => { - event.stopPropagation(); - - const addressCard = event.target.closest('.address-card'); - const addressId = addressCard.dataset.id; - - if (confirm('Are you sure you want to delete this address?')) { - try { - const response = await fetch( - `/account/addresses/delete/${addressId}`, - { - method: 'DELETE', - headers: { - 'Content-Type': 'application/json', - }, - } - ); - - if (response.ok) { - addressCard.remove(); - alert('Address deleted successfully.'); - window.location.reload(); - } else { - alert('Failed to delete address.'); - } - } catch (error) { - console.error('Error:', error); - alert('An error occurred while deleting the address.'); - } - } - }); + if (checkbox && hiddenInput) { + hiddenInput.value = checkbox.checked ? 'true' : 'false'; + } }); -}); +} diff --git a/src/public/js/cart.js b/src/public/js/cart.js index 1586b62..7c94035 100644 --- a/src/public/js/cart.js +++ b/src/public/js/cart.js @@ -14,36 +14,55 @@ function getToast() { }); } -// ── Quantity change ──────────────────────────────────────────────────────────── +// ── Quantity change (with Debouncing and Optimistic UI) ──────────────────────── +const quantityUpdateTimers = {}; + function changeQuantity(productId, change) { const quantityInput = document.getElementById(`quantity-${productId}`); - let newQuantity = parseInt(quantityInput.value, 10) + change; + const currentQuantity = parseInt(quantityInput.value, 10); + let newQuantity = currentQuantity + change; - if (newQuantity < 1) { - newQuantity = 1; + // Local validation + if (newQuantity < 1) return; + if (newQuantity > 5) { + getToast().fire({ icon: 'warning', title: 'Maximum 5 units allowed' }); + return; } - fetch(`/shop/stock?productId=${productId}`) - .then((response) => response.json()) - .then((stockData) => { - const maxQuantity = Math.min(stockData.stock, 5); // enforce per-item cap + // 1. Instant UI update (Optimistic) + quantityInput.value = newQuantity; - if (newQuantity > maxQuantity) { - newQuantity = maxQuantity; - getToast().fire({ - icon: 'warning', - title: `Maximum ${maxQuantity} unit(s) available for this item`, - }); - } + // 2. Clear previous timer for this specific product + if (quantityUpdateTimers[productId]) { + clearTimeout(quantityUpdateTimers[productId]); + } - updateQuantityInDatabase(productId, newQuantity); - }) - .catch(() => { - getToast().fire({ - icon: 'error', - title: 'Error fetching stock information', + // 3. Set a new timer (Debounce) + quantityUpdateTimers[productId] = setTimeout(() => { + // 4. Background check and sync + fetch(`/shop/stock?productId=${productId}`) + .then((response) => response.json()) + .then((stockData) => { + if (newQuantity > stockData.stock) { + // Revert if stock is insufficient + quantityInput.value = stockData.stock; + updateQuantityInDatabase(productId, stockData.stock); + getToast().fire({ + icon: 'warning', + title: `Only ${stockData.stock} units available`, + }); + } else { + updateQuantityInDatabase(productId, newQuantity); + } + }) + .catch(() => { + // Revert to something safe if stock API fails + getToast().fire({ icon: 'error', title: 'Error checking stock' }); }); - }); + + // Cleanup timer reference + delete quantityUpdateTimers[productId]; + }, 500); // 500ms delay } // ── Persist quantity to database ───────────────────────────────────────────── @@ -130,41 +149,33 @@ function updateCartUI(cart) { const quantityInput = document.getElementById(`quantity-${item.productId}`); if (quantityInput) { quantityInput.value = item.quantity; - quantityInput.dataset.lastValid = item.quantity; // store for revert on error + quantityInput.dataset.lastValid = item.quantity; } - const subtotalEl = document.querySelector( - `.cart-item[data-product-id="${item.productId}"] #subtotal-${item.productId}` - ); + const subtotalEl = document.getElementById(`subtotal-${item.productId}`); if (subtotalEl) { subtotalEl.textContent = item.subtotal.toFixed(2); } }); // Summary: items subtotal - const subtotalSummaryEl = document.querySelector( - '.cart-summary .d-flex.justify-content-between span:nth-of-type(2)' - ); - if (subtotalSummaryEl) { + const summaryRows = document.querySelectorAll('.summary-card .summary-row'); + if (summaryRows.length >= 2) { const rawSubtotal = cart.items.reduce( (acc, item) => acc + item.subtotal, 0 ); - subtotalSummaryEl.textContent = `₹${rawSubtotal.toFixed(2)}`; - } + summaryRows[0].querySelector('span:last-child').textContent = + `₹${rawSubtotal.toFixed(2)}`; - // Summary: shipping - const shippingEl = document.querySelector( - '.cart-summary .d-flex.justify-content-between:nth-of-type(2) span:nth-of-type(2)' - ); - if (shippingEl) { - shippingEl.textContent = `₹${cart.shippingCharge.toFixed(2)}`; + // Summary: shipping + const shippingVal = + cart.shippingCharge === 0 ? 'FREE' : `₹${cart.shippingCharge.toFixed(2)}`; + summaryRows[1].querySelector('span:last-child').textContent = shippingVal; } // Summary: grand total - const totalEl = document.querySelector( - '.cart-summary .total span:nth-of-type(2)' - ); + const totalEl = document.querySelector('.summary-total span:last-child'); if (totalEl) { totalEl.textContent = `₹${cart.total.toFixed(2)}`; } @@ -186,13 +197,23 @@ function deleteItem(productId) { getToast().fire({ icon: 'info', title: 'Item removed from cart' }); if (data.cart.items.length === 0) { - // Cart now empty — show the empty state instead of stale summary - const itemsContainer = document.querySelector('.col-md-8.pe-5'); - if (itemsContainer) { - itemsContainer.innerHTML = '

Your cart is empty.

'; + // Cart now empty — show the modern empty state + const container = document.querySelector( + '.shopping-container .container' + ); + if (container) { + container.innerHTML = ` +

Your Cart

+
+
+ +
+

Your cart is empty

+

Seems like you haven't added anything to your cart yet.

+ Shop Now +
+ `; } - // Zero out the summary - updateCartUI({ items: [], shippingCharge: 0, total: 0 }); } else { updateCartUI(data.cart); } diff --git a/src/public/js/checkout.js b/src/public/js/checkout.js index f04119c..fcbaa09 100644 --- a/src/public/js/checkout.js +++ b/src/public/js/checkout.js @@ -7,41 +7,51 @@ document.addEventListener('DOMContentLoaded', () => { // ─── Address Card Selection ─────────────────────────────────────────────────── function setupAddressSelection() { - const addressCards = document.querySelectorAll('.address-card'); + const addressCards = document.querySelectorAll('.address-card-modern'); const selectedAddressIdInput = document.getElementById('selectedAddressId'); + const shippingSection = document.getElementById('shippingDetailsSection'); addressCards.forEach((card) => { card.addEventListener('click', async () => { const isAlreadySelected = card.classList.contains('active-address'); if (isAlreadySelected) { - deselectAddress(card, selectedAddressIdInput); + deselectAddress(card, selectedAddressIdInput, shippingSection); } else { const currentlySelected = document.querySelector( - '.address-card.active-address' + '.address-card-modern.active-address' ); if (currentlySelected) { - deselectAddress(currentlySelected, selectedAddressIdInput); + deselectAddress( + currentlySelected, + selectedAddressIdInput, + shippingSection + ); } - await selectAddress(card, selectedAddressIdInput); + await selectAddress(card, selectedAddressIdInput, shippingSection); } }); }); } -function deselectAddress(card, inputElement) { +function deselectAddress(card, inputElement, shippingSection) { card.classList.remove('active-address'); inputElement.value = ''; + shippingSection.classList.add('d-none'); clearAddressForm(); } -async function selectAddress(card, inputElement) { +async function selectAddress(card, inputElement, shippingSection) { card.classList.add('active-address'); inputElement.value = card.dataset.id; + shippingSection.classList.remove('d-none'); try { const addressData = await fetchAddressDetails(card.dataset.id); fillAddressForm(addressData); + + // Smooth scroll to shipping details + shippingSection.scrollIntoView({ behavior: 'smooth', block: 'nearest' }); } catch (error) { console.error('Error fetching address details:', error); } @@ -76,7 +86,8 @@ function clearAddressForm() { 'landmark', 'zip', ].forEach((id) => { - document.getElementById(id).value = ''; + const el = document.getElementById(id); + if (el) el.value = ''; }); } diff --git a/src/public/js/defaultAddress.js b/src/public/js/defaultAddress.js index 56d0391..c42d7a9 100644 --- a/src/public/js/defaultAddress.js +++ b/src/public/js/defaultAddress.js @@ -1,27 +1,30 @@ document.addEventListener('DOMContentLoaded', () => { - const addressCards = document.querySelectorAll('.address-card'); + const addressCards = document.querySelectorAll('.address-card-modern'); addressCards.forEach((card) => { card.addEventListener('click', async () => { - const addressId = card.getAttribute('data-id'); + // Check if we are on the account page (not checkout) + if (window.location.pathname.includes('/account/addresses')) { + const addressId = card.getAttribute('data-id'); - try { - const response = await fetch('/account/addresses/default', { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - 'X-Requested-With': 'XMLHttpRequest', - }, - body: JSON.stringify({ newDefaultId: addressId }), - }); + try { + const response = await fetch('/account/addresses/default', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'X-Requested-With': 'XMLHttpRequest', + }, + body: JSON.stringify({ newDefaultId: addressId }), + }); - if (response.ok) { - window.location.reload(); - } else { - console.error('Failed to update default address'); + if (response.ok) { + window.location.reload(); + } else { + console.error('Failed to update default address'); + } + } catch (error) { + console.error('Error:', error); } - } catch (error) { - console.error('Error:', error); } }); }); diff --git a/src/public/js/payment.js b/src/public/js/payment.js index af17549..4bf05f5 100644 --- a/src/public/js/payment.js +++ b/src/public/js/payment.js @@ -25,7 +25,7 @@ btn.disabled = disabled; btn.innerHTML = disabled ? `${text}` - : `${text}`; + : `${text}`; }; // ─── Redirect to success ─────────────────────────────────────────────────── @@ -34,16 +34,26 @@ }; // ─── Form submit ─────────────────────────────────────────────────────────── - document - .querySelector('.payment-form') - .addEventListener('submit', async (e) => { + const paymentForm = document.getElementById('paymentForm'); + if (paymentForm) { + paymentForm.addEventListener('submit', async (e) => { e.preventDefault(); const Toast = makeToast(); const confirmBtn = document.getElementById('confirmBtn'); - const paymentMethod = document.querySelector( + const checkedInput = document.querySelector( 'input[name="paymentMethod"]:checked' - ).value; + ); + + if (!checkedInput) { + Toast.fire({ + icon: 'warning', + title: 'Please select a payment method.', + }); + return; + } + + const paymentMethod = checkedInput.value; setButtonState(confirmBtn, true, 'Processing…'); @@ -61,9 +71,10 @@ icon: 'error', title: 'An unexpected error occurred. Please try again.', }); - setButtonState(confirmBtn, false, 'Confirm and Pay'); + setButtonState(confirmBtn, false, 'Confirm and Place Order'); } }); + } // ─── COD Handler ─────────────────────────────────────────────────────────── async function handleCOD(confirmBtn, Toast) { @@ -82,14 +93,14 @@ icon: 'error', title: result.message || 'Failed to place COD order.', }); - setButtonState(confirmBtn, false, 'Confirm and Pay'); + setButtonState(confirmBtn, false, 'Confirm and Place Order'); } } catch { Toast.fire({ icon: 'error', title: 'Failed to place COD order. Please try again.', }); - setButtonState(confirmBtn, false, 'Confirm and Pay'); + setButtonState(confirmBtn, false, 'Confirm and Place Order'); } } @@ -110,14 +121,14 @@ icon: 'error', title: result.message || 'Failed to process wallet payment.', }); - setButtonState(confirmBtn, false, 'Confirm and Pay'); + setButtonState(confirmBtn, false, 'Confirm and Place Order'); } } catch { Toast.fire({ icon: 'error', title: 'Failed to process wallet payment. Please try again.', }); - setButtonState(confirmBtn, false, 'Confirm and Pay'); + setButtonState(confirmBtn, false, 'Confirm and Place Order'); } } @@ -139,7 +150,7 @@ icon: 'error', title: data.message || 'Failed to initiate payment.', }); - setButtonState(confirmBtn, false, 'Confirm and Pay'); + setButtonState(confirmBtn, false, 'Confirm and Place Order'); return; } @@ -150,7 +161,7 @@ icon: 'error', title: 'Failed to connect to payment server. Please try again.', }); - setButtonState(confirmBtn, false, 'Confirm and Pay'); + setButtonState(confirmBtn, false, 'Confirm and Place Order'); return; } @@ -195,7 +206,7 @@ text: 'We could not verify your payment. If money was deducted, please contact support.', confirmButtonText: 'OK', }); - setButtonState(confirmBtn, false, 'Confirm and Pay'); + setButtonState(confirmBtn, false, 'Confirm and Place Order'); } else if (verifyResult.autoRefunded === true) { // ✅ Payment captured but order failed — refund was auto-initiated Swal.fire({ @@ -226,7 +237,7 @@ window.location.href = '/account'; } }); - setButtonState(confirmBtn, false, 'Confirm and Pay'); + setButtonState(confirmBtn, false, 'Confirm and Place Order'); } else { // Generic fallback Swal.fire({ @@ -237,7 +248,7 @@ 'Something went wrong. Please contact support.', confirmButtonText: 'OK', }); - setButtonState(confirmBtn, false, 'Confirm and Pay'); + setButtonState(confirmBtn, false, 'Confirm and Place Order'); } } catch { Swal.fire({ @@ -246,7 +257,7 @@ text: 'Payment may have been deducted. Please contact support with your payment ID.', confirmButtonText: 'OK', }); - setButtonState(confirmBtn, false, 'Confirm and Pay'); + setButtonState(confirmBtn, false, 'Confirm and Place Order'); } }, @@ -264,7 +275,7 @@ icon: 'info', title: 'Payment cancelled. You can try again.', }); - setButtonState(confirmBtn, false, 'Confirm and Pay'); + setButtonState(confirmBtn, false, 'Confirm and Place Order'); }, }, }; diff --git a/src/public/js/shop.js b/src/public/js/shop.js index a22bbbb..b7e88f8 100644 --- a/src/public/js/shop.js +++ b/src/public/js/shop.js @@ -135,51 +135,47 @@ function renderProductCards(products, { append = false } = {}) { offer.discountType === 'percentage' ? `-${offer.discountValue}%` : `-₹${Number(offer.discountValue).toFixed(2)}`; - return `${label}`; + return `${label}`; })(); const priceHtml = hasOffer - ? `
₹${p.discountedPrice.toFixed(2)}
- ₹${p.price.toFixed(2)}` - : `
₹${p.price.toFixed(2)}
`; + ? `₹${p.discountedPrice.toFixed(2)} + ₹${p.price.toFixed(2)}` + : `₹${p.price.toFixed(2)}`; - const stockHtml = isOos - ? 'Out of Stock' - : `${p.stock} Available`; + const stockClass = isOos ? 'out-of-stock' : 'in-stock'; + const stockText = isOos ? 'Out of Stock' : `${p.stock} Available`; const cardClass = isOos - ? 'card h-100 shadow-sm border-1 out-of-stock' - : 'card h-100 shadow-sm border-1'; + ? 'product-card h-100 is-out-of-stock' + : 'product-card h-100'; const btnDisabled = isOos ? 'disabled' : ''; return `
-
- + + -
-
${p.name}
-
${priceHtml}
-
${stockHtml}
-
-
`; @@ -227,6 +223,9 @@ function clearFilters() { } applyFilters(); + if (window.innerWidth < 992) { + closeFilterDrawer(); + } } function setupInfiniteScroll() { @@ -314,3 +313,20 @@ document.addEventListener('DOMContentLoaded', () => { } }); }); + +// ─── Mobile filter drawer ──────────────────────────────────────────────────── +function openFilterDrawer() { + const col = document.getElementById('filterCol'); + const backdrop = document.getElementById('filterBackdrop'); + if (col) col.classList.add('is-open'); + if (backdrop) backdrop.classList.add('is-visible'); + document.body.classList.add('filter-drawer-open'); +} + +function closeFilterDrawer() { + const col = document.getElementById('filterCol'); + const backdrop = document.getElementById('filterBackdrop'); + if (col) col.classList.remove('is-open'); + if (backdrop) backdrop.classList.remove('is-visible'); + document.body.classList.remove('filter-drawer-open'); +} diff --git a/src/public/js/wishlist.js b/src/public/js/wishlist.js index 3f94c3b..3456521 100644 --- a/src/public/js/wishlist.js +++ b/src/public/js/wishlist.js @@ -20,18 +20,20 @@ function removeItemFromDOM(productId) { el.remove(); } - // If no more items, swap list area with the empty-state message - const remaining = document.querySelectorAll('.wishlist-item'); + // If no more items, swap list area with the modern empty-state message + const remaining = document.querySelectorAll('[id^="wishlist-item-"]'); if (remaining.length === 0) { - const container = document.querySelector('.container.my-4'); + const container = document.querySelector('.container.px-lg-5'); if (container) { container.innerHTML = ` -
-

Your Wishlist

-
-