diff --git a/bookshelf-frontend/src/App.jsx b/bookshelf-frontend/src/App.jsx index b8ca496..3f14d8e 100644 --- a/bookshelf-frontend/src/App.jsx +++ b/bookshelf-frontend/src/App.jsx @@ -1,3 +1,5 @@ +import { useMemo, useState } from 'react'; +import { useNavigate } from 'react-router-dom'; import { useState } from 'react'; import { Routes, Route } from 'react-router-dom'; @@ -25,6 +27,18 @@ import OrderHistory from './pages/OrderHistory.jsx'; import './App.css'; export default function App() { + const [activeGenre, setActiveGenre] = useState('All'); + const [cart, setCart] = useState([]); + const navigate = useNavigate(); + + const visibleBooks = useMemo(() => { + if (activeGenre === 'All') return books; + return books.filter((book) => book.genre === activeGenre); + }, [activeGenre]); + + function handleAddToCart(book) { + setCart((prev) => [...prev, book]); + } const [searchQuery, setSearchQuery] = useState(''); return ( @@ -33,6 +47,7 @@ export default function App() { + navigate('/checkout')} /> { + // Automatically skip the gateway if the user is authenticated + const isAuth = localStorage.getItem('isAuthenticated'); + if (isAuth) { + onProceedToAuth(); + } + }, [onProceedToAuth]); + + return ( +
+
+

Checkout

+

Choose how you would like to proceed with your order.

+ +
+
+

Already have an account?

+

Log in for faster checkout and to track your order history.

+ +
+ +
OR
+ +
+

New here?

+

Create an account to save your details for future purchases.

+ +
+ +
OR
+ +
+

Fast checkout without creating an account

+

You can complete your purchase as a guest. No account required.

+ +
+
+
+
+ ); +} diff --git a/bookshelf-frontend/src/components/GuestCheckoutForm.css b/bookshelf-frontend/src/components/GuestCheckoutForm.css new file mode 100644 index 0000000..85364c0 --- /dev/null +++ b/bookshelf-frontend/src/components/GuestCheckoutForm.css @@ -0,0 +1,93 @@ +.guest-checkout { + background: var(--paper-dim); + border: 1px solid var(--line); + border-radius: var(--radius-card); + padding: 40px; + max-width: 600px; + width: 100%; + margin: 40px auto; +} + +.guest-checkout__title { + font-family: var(--font-display); + font-size: 24px; + color: var(--ink); + margin-bottom: 8px; +} + +.guest-checkout__subtitle { + color: var(--ink-soft); + margin-bottom: 24px; + font-size: 14px; +} + +.guest-checkout__form { + display: flex; + flex-direction: column; + gap: 20px; +} + +.form-group { + display: flex; + flex-direction: column; + gap: 8px; +} + +.form-row { + display: flex; + gap: 20px; +} + +.form-row .form-group { + flex: 1; +} + +.guest-checkout label { + font-size: 14px; + font-weight: 600; + color: var(--ink); +} + +.guest-checkout input { + padding: 12px; + border: 1px solid var(--line); + border-radius: 6px; + background: var(--paper); + color: var(--ink); + font-family: var(--font-body); + font-size: 14px; + transition: border-color 0.2s ease, box-shadow 0.2s ease; +} + +.guest-checkout input:focus { + outline: none; + border-color: var(--ink); + box-shadow: 0 0 0 2px rgba(0, 0, 0, 0.05); +} + +.guest-checkout input.error { + border-color: #e53e3e; +} + +.error-text { + color: #e53e3e; + font-size: 12px; +} + +.btn-submit { + margin-top: 10px; + background: var(--ink); + color: var(--paper); + border: none; + padding: 14px; + border-radius: 6px; + font-weight: 600; + cursor: pointer; + transition: opacity 0.2s ease; + width: 100%; + font-size: 16px; +} + +.btn-submit:hover { + opacity: 0.9; +} diff --git a/bookshelf-frontend/src/components/GuestCheckoutForm.jsx b/bookshelf-frontend/src/components/GuestCheckoutForm.jsx new file mode 100644 index 0000000..ae7e5bd --- /dev/null +++ b/bookshelf-frontend/src/components/GuestCheckoutForm.jsx @@ -0,0 +1,132 @@ +import { useState } from 'react'; +import './GuestCheckoutForm.css'; + +export default function GuestCheckoutForm({ onOrderComplete }) { + const [formData, setFormData] = useState({ + email: '', + fullName: '', + address: '', + city: '', + postalCode: '', + }); + + const [errors, setErrors] = useState({}); + + const validate = () => { + const newErrors = {}; + if (!formData.email || !/\S+@\S+\.\S+/.test(formData.email)) { + newErrors.email = 'Valid email is required.'; + } + if (!formData.fullName.trim()) newErrors.fullName = 'Full Name is required.'; + if (!formData.address.trim()) newErrors.address = 'Address is required.'; + if (!formData.city.trim()) newErrors.city = 'City is required.'; + if (!formData.postalCode.trim()) newErrors.postalCode = 'Postal Code is required.'; + return newErrors; + }; + + const handleSubmit = (e) => { + e.preventDefault(); + const newErrors = validate(); + if (Object.keys(newErrors).length > 0) { + setErrors(newErrors); + return; + } + + // Simulate successful order + const mockOrderPayload = { + customerType: 'guest', + ...formData + }; + + console.log('Guest order submitted:', mockOrderPayload); + onOrderComplete(); + }; + + const handleChange = (e) => { + const { name, value } = e.target; + setFormData(prev => ({ ...prev, [name]: value })); + if (errors[name]) { + setErrors(prev => ({ ...prev, [name]: null })); + } + }; + + return ( +
+

Guest Checkout

+

Please enter your shipping and contact information.

+ +
+
+ + + {errors.email && {errors.email}} +
+ +
+ + + {errors.fullName && {errors.fullName}} +
+ +
+ + + {errors.address && {errors.address}} +
+ +
+
+ + + {errors.city && {errors.city}} +
+ +
+ + + {errors.postalCode && {errors.postalCode}} +
+
+ + +
+
+ ); +} diff --git a/bookshelf-frontend/src/main.jsx b/bookshelf-frontend/src/main.jsx index 6254da5..f70a636 100644 --- a/bookshelf-frontend/src/main.jsx +++ b/bookshelf-frontend/src/main.jsx @@ -1,3 +1,12 @@ +import React from 'react' +import ReactDOM from 'react-dom/client' +import { BrowserRouter, Routes, Route } from 'react-router-dom' +import App from './App.jsx' +import AboutUs from './pages/AboutUs.jsx' +import PrivacyPolicy from './pages/PrivacyPolicy.jsx' +import TermsOfService from './pages/TermsOfService.jsx' +import Checkout from './pages/Checkout.jsx' +import './index.css' import { StrictMode } from 'react'; import { createRoot } from 'react-dom/client'; import { BrowserRouter } from 'react-router-dom'; @@ -10,6 +19,13 @@ import './index.css'; createRoot(document.getElementById('root')).render( + + } /> + } /> + } /> + } /> + } /> + diff --git a/bookshelf-frontend/src/pages/Checkout.css b/bookshelf-frontend/src/pages/Checkout.css index 5a0c6ea..8940864 100644 --- a/bookshelf-frontend/src/pages/Checkout.css +++ b/bookshelf-frontend/src/pages/Checkout.css @@ -1,3 +1,12 @@ +.checkout-page { + min-height: 100vh; + display: flex; + flex-direction: column; +} + +.checkout-main { + flex: 1; + padding: 40px 20px; .checkout-container { max-width: 800px; margin: 40px auto; @@ -203,6 +212,23 @@ .checkout-success { text-align: center; + margin-top: 100px; +} + +.checkout-success h2 { + font-family: var(--font-display); + font-size: 32px; + margin-bottom: 16px; + color: var(--ink); +} + +.checkout-success p { + color: var(--ink-soft); + margin-bottom: 32px; +} + +.checkout-success .btn-primary { + display: inline-block; padding: 40px 20px; } diff --git a/bookshelf-frontend/src/pages/Checkout.jsx b/bookshelf-frontend/src/pages/Checkout.jsx index e4b5539..b7f8e4d 100644 --- a/bookshelf-frontend/src/pages/Checkout.jsx +++ b/bookshelf-frontend/src/pages/Checkout.jsx @@ -1,3 +1,56 @@ +import { useState } from 'react'; +import Navbar from '../components/Navbar'; +import Footer from '../components/Footer'; +import CheckoutGateway from '../components/CheckoutGateway'; +import GuestCheckoutForm from '../components/GuestCheckoutForm'; +import './Checkout.css'; + +export default function Checkout() { + const [checkoutStep, setCheckoutStep] = useState('gateway'); // 'gateway', 'form', 'success' + + const handleProceedToGuest = () => { + setCheckoutStep('form'); + }; + + const handleProceedToAuth = () => { + // If we had an authenticated checkout form, we'd go there. + // For this mock, we'll just go straight to the form step since the guest form can be reused or skipped if they already have saved addresses. + setCheckoutStep('form'); + }; + + const handleOrderComplete = () => { + setCheckoutStep('success'); + }; + + return ( +
+ {}} /> +
+ +
+ {checkoutStep === 'gateway' && ( + + )} + + {checkoutStep === 'form' && ( + + )} + + {checkoutStep === 'success' && ( +
+

Order Placed Successfully!

+

Thank you for your purchase.

+ +
+ )} +
+ +