Capstone 3 Added

master
Ron Reciproco 11 months ago
parent 12b87b8d2f
commit 82a05cde7f

@ -3,39 +3,51 @@ const User = require("../model/User")
exports.createOrder = async (req, res) => { exports.createOrder = async (req, res) => {
try { try {
const { userId, products, totalAmount } = req.body const {
userId,
products,
totalAmount,
shippingAddress,
personalInfo,
shippingMethod,
paymentMethod,
// Add other details as needed
} = req.body;
const user = await User.findById(userId) const user = await User.findById(userId);
if (!user) { if (!user) {
return res.status(404).json({ message: "User not found" }) return res.status(404).json({ message: "User not found" });
} }
// Check if the user is an admin // Check if the user is an admin
if (user.isAdmin) { if (user.isAdmin) {
return res return res.status(403).json({ message: "Admins cannot create orders" });
.status(403)
.json({ message: "Admins cannot create orders" })
} }
const newOrder = { const newOrder = {
products: products, products: products,
totalAmount: totalAmount, totalAmount: totalAmount,
shippingAddress: shippingAddress,
personalInfo: personalInfo,
shippingMethod: shippingMethod,
paymentMethod: paymentMethod,
// Add other details to newOrder as needed
purchaseOn: Date.now(), purchaseOn: Date.now(),
} };
user.orderedProducts.push(newOrder) user.orderedProducts.push(newOrder);
await user.save() await user.save();
res.status(201).json({ res.status(201).json({
message: "Order created successfully", message: "Order created successfully",
order: newOrder, order: newOrder,
}) });
} catch (error) { } catch (error) {
console.error(error) console.error(error);
res.status(500).json({ message: "Internal Server Error" }) res.status(500).json({ message: "Internal Server Error" });
} }
} };
// Retrieve authenticated user's orders // Retrieve authenticated user's orders
exports.getOrders = async (req, res) => { exports.getOrders = async (req, res) => {

@ -532,4 +532,3 @@ exports.updateAddress = async (req, res) => {
res.status(500).json({ message: 'Internal server error' }); res.status(500).json({ message: 'Internal server error' });
} }
}; };

@ -0,0 +1,13 @@
// models/ShippingMethod.js
const mongoose = require('mongoose');
const shippingMethodSchema = new mongoose.Schema({
name: { type: String, required: true },
description: { type: String, required: true },
// Add other fields as needed
});
const ShippingMethod = mongoose.model('ShippingMethod', shippingMethodSchema);
module.exports = ShippingMethod;

@ -18,6 +18,7 @@ import UpdateProfile from './components/UpdateProfile';
import AdminDashboard from './pages/AdminDashboard'; import AdminDashboard from './pages/AdminDashboard';
import ProductPage from './pages/ProductPage'; import ProductPage from './pages/ProductPage';
import CheckoutPage from './pages/CheckoutPage'; import CheckoutPage from './pages/CheckoutPage';
import OrderConfirmationPage from './pages/OrderConfirmationPage';
import Error from './pages/Error'; // Import the NotFound component import Error from './pages/Error'; // Import the NotFound component
import { ToastContainer } from 'react-toastify'; import { ToastContainer } from 'react-toastify';
import 'react-toastify/dist/ReactToastify.css'; import 'react-toastify/dist/ReactToastify.css';
@ -108,6 +109,7 @@ function App() {
<Route path="/404" element={<Error />} /> <Route path="/404" element={<Error />} />
<Route path="/product/:productId" element={<ProductPage />} /> <Route path="/product/:productId" element={<ProductPage />} />
<Route path="/checkout" element={<CheckoutPage />} /> <Route path="/checkout" element={<CheckoutPage />} />
<Route path="/order-confirmation" element={<OrderConfirmationPage />} />
{/* Catch-all route that redirects to the home page */} {/* Catch-all route that redirects to the home page */}
<Route path="*" element={<Navigate to="/404" />} /> <Route path="*" element={<Navigate to="/404" />} />
</Routes> </Routes>

@ -0,0 +1,134 @@
import React from "react";
import { Button, Collapse, Col, Row, Table, Form } from "react-bootstrap";
import AddressForm from "./AddressForm"; // Assuming the path is correct
const AddressCollapse = ({
addressOpen,
setAddressOpen,
isCustomerLoggedIn,
selectedAddress,
setSelectedAddress,
handleContinueAddress,
addAddressFormOpen,
setAddAddressFormOpen,
addressFormError,
addresses,
handleDeleteAddress,
}) => {
const handleAddressSelect = (addressId) => {
// Update the selectedAddressId
setSelectedAddress(addressId);
// If you also want to update the selectedAddress object, find the corresponding address
const selected = addresses.find((address) => address._id === addressId);
setSelectedAddress(selected);
};
return (
<div className="border rounded mb-3 p-3">
<h3>
<div className="d-flex justify-content-between align-items-start">
<span className="fs-6">2. Address</span>
<Button
variant="link"
onClick={() => setAddressOpen(!addressOpen)}
aria-controls="addressCollapse"
aria-expanded={addressOpen}
>
<i className={`bi bi-chevron-${addressOpen ? "up" : "down"}`}></i>
</Button>
</div>
</h3>
<Collapse in={addressOpen}>
<div id="addressCollapse">
{isCustomerLoggedIn ? (
<Row>
<span className="mb-2" style={{ fontSize: "16px" }}>
The selected address will be used both as your personal address
(for invoice) and as your delivery address.
</span>
<Col className="border rounded mb-3 p-3" md={6}>
{/* Left column - Add New Address Form Dropdown */}
<div>
{addAddressFormOpen && (
<AddressForm
selectedAddress={selectedAddress}
setSelectedAddress={setSelectedAddress}
handleContinueAddress={handleContinueAddress}
addressFormError={addressFormError}
/>
)}
<Button
className="my-3"
variant="primary"
onClick={() => setAddAddressFormOpen(!addAddressFormOpen)}
>
Add New Address
</Button>
</div>
</Col>
<Col md={6}>
{/* Right column - My Addresses from addressForm */}
{addresses && addresses.length > 0 ? (
<Table striped bordered hover>
<thead>
<tr>
<th>My Addresses</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
{addresses.map((address, index) => (
<tr key={index}>
<td>
<div className="d-flex justify-content-between align-items-center">
<Form.Check
type="radio"
id={`addressRadio${index}`}
name="addressRadio"
checked={
selectedAddress &&
selectedAddress._id === address._id
}
onChange={() =>
handleAddressSelect(address._id)
}
/>
<p className="mx-3">
{address.street}, {address.city},{" "}
{address.state}, {address.zipCode},{" "}
{address.country}
</p>
</div>
</td>
<td>
<Button
variant="link"
className="text-danger"
onClick={() => handleDeleteAddress(address._id)}
>
<i className="bi bi-trash"></i>
</Button>
</td>
</tr>
))}
</tbody>
</Table>
) : (
<p>No existing addresses. Add a new address below.</p>
)}
</Col>
</Row>
) : (
<p>User not logged in. Log in to view and manage addresses.</p>
)}
</div>
</Collapse>
</div>
);
};
export default AddressCollapse;

@ -1,6 +1,5 @@
import React from 'react'; import React from "react";
import { Form, Button } from 'react-bootstrap'; import { Form, Button } from "react-bootstrap";
const AddressForm = ({ const AddressForm = ({
selectedAddress, selectedAddress,
@ -92,7 +91,8 @@ const AddressForm = ({
"Zamboanga del Sur", "Zamboanga del Sur",
"Zamboanga Sibugay", "Zamboanga Sibugay",
]; ];
// Define the array of Philippine states
const countryStates = ["...", "Philippines"];
return ( return (
<Form onSubmit={handleContinueAddress}> <Form onSubmit={handleContinueAddress}>
@ -142,6 +142,7 @@ const AddressForm = ({
required required
/> />
</Form.Group> </Form.Group>
<Form.Group controlId="state"> <Form.Group controlId="state">
<Form.Label>State</Form.Label> <Form.Label>State</Form.Label>
<Form.Control <Form.Control
@ -160,9 +161,27 @@ const AddressForm = ({
))} ))}
</Form.Control> </Form.Control>
</Form.Group> </Form.Group>
<Form.Group controlId="country"> <Form.Group controlId="country">
<Form.Label>Country</Form.Label> <Form.Label>Country</Form.Label>
<Form.Control type="text" placeholder="Philippines" disabled /> <Form.Control
as="select"
value={selectedAddress?.country || ""}
onChange={(e) =>
setSelectedAddress({
...selectedAddress,
country: e.target.value, // Fix: change state to country
})
}
required
>
<option value="">Select Country</option>
{countryStates.map((country, index) => (
<option key={index} value={country}>
{country}
</option>
))}
</Form.Control>
</Form.Group> </Form.Group>
<Button className="mt-3" variant="primary" type="submit"> <Button className="mt-3" variant="primary" type="submit">

@ -0,0 +1,38 @@
const BankOption = ({ totalPrice }) => {
return (
<div>
<h4>Bank Payment Details</h4>
<div style={{ border: '1px solid #ddd', padding: '10px', borderRadius: '5px', marginBottom: '10px' }}>
<p>
Please transfer the amount to the following bank account:
</p>
<div>
<strong>Bank:</strong> BPI Bank
</div>
<div>
<strong>Account Holder:</strong> istore philippines
</div>
<div>
<strong>Account Number:</strong> 1234-1234-1234-1234
</div>
<div>
<strong>Amount:</strong> {new Intl.NumberFormat('en-PH', {
style: 'currency',
currency: 'PHP',
}).format(totalPrice)}
</div>
</div>
<div className="mt-3">
<p>
After completing the bank transfer, please submit the reference number{' '}
<a href="mailto:customercare@istore.com.ph">customercare@istore.com.ph</a>.
</p>
</div>
</div>
);
};
export default BankOption;

@ -3,7 +3,7 @@ import { Modal, Button, Form } from "react-bootstrap";
const EditAddress = ({ show, onHide, onSave, address }) => { const EditAddress = ({ show, onHide, onSave, address }) => {
const [editedAddress, setEditedAddress] = useState({ ...address }); const [editedAddress, setEditedAddress] = useState({ ...address });
const [updateStatus, setUpdateStatus] = useState(null); const [setUpdateStatus] = useState(null);
useEffect(() => { useEffect(() => {
setEditedAddress({ ...address }); setEditedAddress({ ...address });

@ -0,0 +1,73 @@
// OrderDetails.js
import React from "react";
import PropTypes from "prop-types";
const OrderDetails = ({ orderData }) => {
if (!orderData) {
// Handle the case where orderData is not available
return <div>Loading...</div>;
}
const {
userDetails,
selectedAddress,
selectedShippingMethod,
cartItems,
appliedPromoCode,
selectedPaymentOption,
termsAgreed,
// Include any other necessary data
} = orderData;
return (
<div>
<h2>Order Details</h2>
<h3>User Details</h3>
<pre>{JSON.stringify(userDetails, null, 2)}</pre>
<h3>Selected Address</h3>
<pre>{JSON.stringify(selectedAddress, null, 2)}</pre>
<h3>Selected Shipping Method</h3>
<pre>{selectedShippingMethod}</pre>
<h3>Cart Items</h3>
<ul>
{cartItems.map((item, index) => (
<li key={index}>
<h4>Item {index + 1}</h4>
<pre>{JSON.stringify(item, null, 2)}</pre>
</li>
))}
</ul>
<h3>Applied Promo Code</h3>
<pre>{appliedPromoCode}</pre>
<h3>Selected Payment Option</h3>
<pre>{selectedPaymentOption}</pre>
<h3>Terms Agreed</h3>
<pre>{termsAgreed ? "Agreed" : "Not Agreed"}</pre>
{/* Include additional sections as needed for other data */}
</div>
);
};
// Specify PropTypes for the orderData prop
OrderDetails.propTypes = {
orderData: PropTypes.shape({
userDetails: PropTypes.object,
selectedAddress: PropTypes.object,
selectedShippingMethod: PropTypes.string,
cartItems: PropTypes.array,
appliedPromoCode: PropTypes.string,
selectedPaymentOption: PropTypes.string,
termsAgreed: PropTypes.bool,
// Include PropTypes for other data properties
}),
};
export default OrderDetails;

@ -0,0 +1,86 @@
// components/OrderSummary.js
import React from 'react';
import { Col, Table, Form, Button } from 'react-bootstrap';
const OrderSummary = ({
cartItems,
shippingFee,
totalPrice,
discountAmount,
promoCode,
setPromoCode,
handleApplyPromoCode,
appliedPromoCode,
}) => {
return (
<Col xs={12} md={4} className="text-start">
<h3>Order Summary</h3>
<Table striped bordered hover>
<tbody>
<tr>
<td>Total Items</td>
<td>
{cartItems.reduce((total, item) => total + item.quantity, 0)}
</td>
</tr>
<tr>
<td>Shipping Fee</td>
<td>{new Intl.NumberFormat('en-PH', {
style: 'currency',
currency: 'PHP',
}).format(shippingFee.value)}</td>
</tr>
<tr>
<td>Shipping Details</td>
<td>{shippingFee.details}</td>
</tr>
<tr>
<td>Total</td>
<td>
<strong>{new Intl.NumberFormat('en-PH', {
style: 'currency',
currency: 'PHP',
}).format(totalPrice)}</strong>
</td>
</tr>
<tr>
<td>Discount</td>
<td>
<strong>{discountAmount}%</strong>
</td>
</tr>
<tr>
<td colSpan="2">
<Form.Group controlId="promoCode">
<Form.Label>Promo Code</Form.Label>
<Form.Control
type="text"
placeholder="Enter promo code"
value={promoCode}
onChange={(e) => setPromoCode(e.target.value)}
/>
<Button
className="mt-3"
variant="primary"
onClick={handleApplyPromoCode}
>
Apply Promo Code
</Button>
</Form.Group>
{appliedPromoCode && (
<div>
<p className="mt-2">
Applied Promo Code: <strong>{appliedPromoCode}</strong>
</p>
</div>
)}
</td>
</tr>
</tbody>
</Table>
</Col>
);
};
export default OrderSummary;

@ -0,0 +1,144 @@
import React, { useState, useEffect } from "react";
import { Button, Collapse, Form } from "react-bootstrap";
import { useNavigate } from 'react-router-dom';
import PropTypes from "prop-types"; // Import PropTypes
import BankOption from "./BankOption";
const PaymentCollapse = ({
totalPrice,
userDetails,
selectedAddress,
selectedShippingMethod,
cartItems,
appliedPromoCode,
}) => {
const [paymentOpen, setPaymentOpen] = useState(false);
const [selectedPaymentOption, setSelectedPaymentOption] = useState(null);
const [termsAgreed, setTermsAgreed] = useState(false);
const navigate = useNavigate();
const handlePaymentOptionChange = (option) => {
setSelectedPaymentOption(option);
};
const handleTermsAgreementChange = () => {
setTermsAgreed(!termsAgreed);
};
const isSubmitButtonDisabled = !termsAgreed || selectedPaymentOption === null;
const handleOrderSubmission = () => {
try {
const orderData = {
userDetails,
selectedAddress,
selectedShippingMethod,
cartItems,
appliedPromoCode,
selectedPaymentOption,
termsAgreed,
// Include any other necessary data
};
// Log the order data to the console for testing
console.log("Order Data:", orderData);
// Display a success message to the user (for testing)
console.log("Order submission simulated successfully!");
// Navigate to the order confirmation page with orderData
navigate("/order-confirmation", { state: { orderData } });
} catch (error) {
console.error("Error submitting order:", error);
// Handle general error (for testing)
console.log("Order submission simulated failed.");
}
};
return (
<div className="border rounded mb-3 p-3">
<h3>
<div className="d-flex justify-content-between align-items-start">
<span className="fs-6">4. Payment</span>
<Button
variant="link"
onClick={() => setPaymentOpen(!paymentOpen)}
aria-controls="paymentCollapse"
aria-expanded={paymentOpen}
>
<i className={`bi bi-chevron-${paymentOpen ? "up" : "down"}`}></i>
</Button>
</div>
</h3>
<Collapse in={paymentOpen}>
<div id="paymentCollapse">
<Form.Group className="mb-3">
<Form.Label>Select Payment Option:</Form.Label>
<Form.Control
as="select"
value={selectedPaymentOption}
onChange={(e) => handlePaymentOptionChange(e.target.value)}
>
<option value="">Select</option>
<option value="bank">Bank Option</option>
{/* Add other payment options here */}
</Form.Control>
</Form.Group>
{selectedPaymentOption === "bank" && (
<BankOption totalPrice={totalPrice} />
)}
{/* Terms Agreement */}
<Form.Group
className="mb-3"
style={{
backgroundColor: "#343a40",
padding: "10px",
borderRadius: "5px",
}}
>
<Form.Check
type="checkbox"
label="I agree to the terms of service and will adhere to them unconditionally."
checked={termsAgreed}
onChange={handleTermsAgreementChange}
style={{ color: "white" }}
/>
</Form.Group>
{/* Payment Button */}
<Button
variant="primary"
onClick={handleOrderSubmission}
disabled={isSubmitButtonDisabled}
>
Submit Order
</Button>
{/* Message below the Payment Button */}
{!termsAgreed && (
<div className="mt-2 text-danger">
Please agree to the terms of service before submitting the order.
</div>
)}
</div>
</Collapse>
</div>
);
};
// Specify PropTypes for each prop used in the component
PaymentCollapse.propTypes = {
totalPrice: PropTypes.number.isRequired,
userDetails: PropTypes.object,
selectedAddress: PropTypes.object,
selectedShippingMethod: PropTypes.string,
cartItems: PropTypes.array,
appliedPromoCode: PropTypes.string,
};
export default PaymentCollapse;

@ -0,0 +1,60 @@
import React from "react";
import { Button, Collapse } from "react-bootstrap";
const PersonalInfoCollapse = ({ personalInfoOpen, setPersonalInfoOpen, isCustomerLoggedIn, userDetails }) => {
return (
<div className="border rounded mb-3 p-3">
<h3>
<div className="d-flex justify-content-between align-items-start">
<span className="fs-6">1. Personal Information</span>
<Button
variant="link"
onClick={() => setPersonalInfoOpen(!personalInfoOpen)}
aria-controls="personalInfoCollapse"
aria-expanded={personalInfoOpen}
>
<i
className={`bi bi-chevron-${personalInfoOpen ? "up" : "down"}`}
></i>
</Button>
</div>
</h3>
<Collapse in={personalInfoOpen}>
<div id="personalInfoCollapse">
{isCustomerLoggedIn ? (
<div>
<p>
<strong>Name:</strong> {userDetails?.firstName}{" "}
{userDetails?.lastName}
</p>
<p>
<strong>Email:</strong> {userDetails?.email}
</p>
<p>
<strong>Mobile Number:</strong> {userDetails?.mobileNo}
</p>
<p>If you want to update your information</p>
<Button variant="primary" href="/update-profile">
Update Information
</Button>
</div>
) : (
<div className="mx-4">
<p>
You are not logged in. Please log in or register to proceed.
</p>
<Button variant="primary" href="/register">
Register
</Button>
<Button className="mx-2" variant="primary" href="/login">
Login
</Button>
</div>
)}
</div>
</Collapse>
</div>
);
};
export default PersonalInfoCollapse;

@ -0,0 +1,132 @@
import React, { useState } from "react";
import { Button, Collapse } from "react-bootstrap";
const ShippingMethodCollapse = ({ shippingMethodOpen, setShippingMethodOpen, setSelectedShippingMethod }) => {
const [selectedOption, setSelectedOption] = useState(null);
const handleOptionChange = (option) => {
setSelectedOption(option);
setSelectedShippingMethod(option); // Update the parent component state
};
return (
<div className="border rounded mb-3 p-3">
<h3>
<div className="d-flex justify-content-between align-items-start">
<span className="fs-6">3. Shipping Method</span>
<Button
variant="link"
onClick={() => setShippingMethodOpen(!shippingMethodOpen)}
aria-controls="shippingMethodCollapse"
aria-expanded={shippingMethodOpen}
>
<i
className={`bi bi-chevron-${shippingMethodOpen ? "up" : "down"}`}
></i>
</Button>
</div>
</h3>
<Collapse in={shippingMethodOpen}>
<div id="shippingMethodCollapse">
{/* Infographic-style table for Shipping Methods */}
<div className="row">
{/* Option 1 */}
<div className="col-md-4 mb-3">
<div
className="border p-3 text-center"
style={{ height: "auto" }}
>
<div className="d-grid gap-2 justify-content-center">
<input
type="checkbox"
className="form-check-input d-flex justify-content-center mx-auto fs-5"
id="option1Checkbox"
checked={selectedOption === "courier"}
onChange={() => handleOptionChange("courier")}
/>
<i
className="bi bi-truck text-primary"
style={{ fontSize: "3.7rem" }}
></i>
<label
className="form-check-label"
htmlFor="option1Checkbox"
>
<strong> Pickup by Courier (Lalamove/Grab)</strong>
<br />
Note: Customer pays for shipping. Wait for us to notify you
that your order is ready before you book your courier.
</label>
</div>
</div>
</div>
{/* Option 2 */}
<div className="col-md-4 mb-3">
<div
className="border p-3 text-center"
style={{ height: "auto" }}
>
<div className="d-grid gap-2 justify-content-center">
<input
type="checkbox"
className="form-check-input d-flex justify-content-center mx-auto fs-5"
id="option2Checkbox"
checked={selectedOption === "curbside"}
onChange={() => handleOptionChange("curbside")}
/>
<i
className="bi bi-shop text-primary"
style={{ fontSize: "3.7rem" }}
></i>
<label
className="form-check-label"
htmlFor="option2Checkbox"
>
<strong>Curbside Pickup</strong>
<br />
Note: Wait for us to notify you that your order is ready
before proceeding to our designated pickup area in front of
our building.
</label>
</div>
</div>
</div>
{/* Option 3 */}
<div className="col-md-4 mb-3">
<div
className="border p-3 text-center"
style={{ height: "auto" }}
>
<div className="d-grid gap-2 justify-content-center">
<input
type="checkbox"
className="form-check-input d-flex justify-content-center mx-auto fs-5"
id="option3Checkbox"
checked={selectedOption === "express"}
onChange={() => handleOptionChange("express")}
/>
<i
className="bi bi-clock text-primary"
style={{ fontSize: "3.7rem" }}
></i>
<label
className="form-check-label"
htmlFor="option3Checkbox"
>
<strong>Local Express Delivery</strong>
<br />
within 24 - 48 hours during business days
</label>
</div>
</div>
</div>
</div>
</div>
</Collapse>
</div>
);
};
export default ShippingMethodCollapse;

@ -3,14 +3,12 @@ import {
Container, Container,
Row, Row,
Col, Col,
Table,
Button,
Collapse,
Form,
Modal,
} from "react-bootstrap"; } from "react-bootstrap";
import AddressForm from "../components/AddressForm"; import OrderSummary from "../components/OrderSummary";
import EditAddress from "../components/EditAddress"; import PersonalInfoCollapse from "../components/PersonalInfoCollapse";
import AddressCollapse from "../components/AddressCollapse";
import PaymentCollapse from "../components/PaymentCollapse";
import ShippingMethodCollapse from "../components/ShippingMethodCollapse";
import { jwtDecode } from "jwt-decode"; import { jwtDecode } from "jwt-decode";
import { toast, ToastContainer } from "react-toastify"; import { toast, ToastContainer } from "react-toastify";
import "react-toastify/dist/ReactToastify.css"; import "react-toastify/dist/ReactToastify.css";
@ -18,14 +16,14 @@ import "react-toastify/dist/ReactToastify.css";
const CheckoutPage = () => { const CheckoutPage = () => {
const [cartItems, setCartItems] = useState([]); const [cartItems, setCartItems] = useState([]);
const [totalPrice, setTotalPrice] = useState(0); const [totalPrice, setTotalPrice] = useState(0);
const [shippingFee, setShippingFee] = useState({ const [shippingFee] = useState({
value: 200, value: 200,
details: "Fixed shipping fee of ₱200", details: "Fixed shipping fee of ₱200",
}); });
const [promoCode, setPromoCode] = useState(""); const [promoCode, setPromoCode] = useState("");
const [personalInfoOpen, setPersonalInfoOpen] = useState(true); const [personalInfoOpen, setPersonalInfoOpen] = useState(true);
const [addressOpen, setAddressOpen] = useState(false); const [addressOpen, setAddressOpen] = useState(false);
const [paymentOpen, setPaymentOpen] = useState(false); const [shippingMethodOpen, setShippingMethodOpen] = useState(false);
const [isCustomerLoggedIn, setIsCustomerLoggedIn] = useState(false); const [isCustomerLoggedIn, setIsCustomerLoggedIn] = useState(false);
const [userDetails, setUserDetails] = useState(null); const [userDetails, setUserDetails] = useState(null);
const [appliedPromoCode, setAppliedPromoCode] = useState(null); const [appliedPromoCode, setAppliedPromoCode] = useState(null);
@ -33,13 +31,9 @@ const CheckoutPage = () => {
const [discountAmount, setDiscountAmount] = useState(0); const [discountAmount, setDiscountAmount] = useState(0);
const [addAddressFormOpen, setAddAddressFormOpen] = useState(false); const [addAddressFormOpen, setAddAddressFormOpen] = useState(false);
const [addresses, setAddresses] = useState([]); const [addresses, setAddresses] = useState([]);
const [selectedAddressId, setSelectedAddressId] = useState(null); const [addressFormError] = useState(null);
const [addressFormError, setAddressFormError] = useState(null); const [selectedAddress, setSelectedAddress] = useState(null);
const [selectedShippingMethod, setSelectedShippingMethod] = useState(null);
const handleAddressSelect = (addressId) => {
setSelectedAddressId(addressId);
};
const handleDeleteAddress = async (addressId) => { const handleDeleteAddress = async (addressId) => {
try { try {
@ -91,15 +85,6 @@ const CheckoutPage = () => {
} }
}; };
const [selectedAddress, setSelectedAddress] = useState({
street: "",
zipCode: "",
city: "",
state: "",
country: "Philippines",
});
const handleContinueAddress = async (e) => { const handleContinueAddress = async (e) => {
e.preventDefault(); // Prevent the default form submission behavior e.preventDefault(); // Prevent the default form submission behavior
@ -108,7 +93,8 @@ const CheckoutPage = () => {
!selectedAddress.street || !selectedAddress.street ||
!selectedAddress.zipCode || !selectedAddress.zipCode ||
!selectedAddress.city || !selectedAddress.city ||
!selectedAddress.state !selectedAddress.state ||
!selectedAddress.country
) { ) {
console.error("Please fill in all required address fields"); console.error("Please fill in all required address fields");
// Display an error toast // Display an error toast
@ -211,11 +197,6 @@ const CheckoutPage = () => {
const data = await response.json(); const data = await response.json();
setAddresses(data); setAddresses(data);
// Preselect the first address if available
if (data.length > 0) {
handleAddressSelect(data[0].id);
}
return data; // Return the fetched addresses return data; // Return the fetched addresses
} else { } else {
console.error("Failed to fetch addresses:", response.status); console.error("Failed to fetch addresses:", response.status);
@ -455,6 +436,9 @@ const CheckoutPage = () => {
fetchAddresses(); fetchAddresses();
}, [isCustomerLoggedIn]); }, [isCustomerLoggedIn]);
return ( return (
<Container className="my-5 text-start"> <Container className="my-5 text-start">
<h2>Checkout</h2> <h2>Checkout</h2>
@ -462,256 +446,58 @@ const CheckoutPage = () => {
{/* Left side - Collapsible Forms */} {/* Left side - Collapsible Forms */}
<Col xs={12} md={8}> <Col xs={12} md={8}>
{/* Collapsible Table - Personal Information */} {/* Collapsible Table - Personal Information */}
<div className="border rounded mb-3 p-3"> <PersonalInfoCollapse
<h3> personalInfoOpen={personalInfoOpen}
<div className="d-flex justify-content-between align-items-start"> setPersonalInfoOpen={setPersonalInfoOpen}
<span className="fs-6">1. Personal Information</span> isCustomerLoggedIn={isCustomerLoggedIn}
<Button userDetails={userDetails}
variant="link" />
onClick={() => setPersonalInfoOpen(!personalInfoOpen)}
aria-controls="personalInfoCollapse"
aria-expanded={personalInfoOpen}
>
<i
className={`bi bi-chevron-${
personalInfoOpen ? "up" : "down"
}`}
></i>
</Button>
</div>
</h3>
<Collapse in={personalInfoOpen}>
<div id="personalInfoCollapse">
{isCustomerLoggedIn ? (
<div>
<p>
<strong>Name:</strong> {userDetails?.firstName}{" "}
{userDetails?.lastName}
</p>
<p>
<strong>Email:</strong> {userDetails?.email}
</p>
<p>
<strong>Mobile Number:</strong> {userDetails?.mobileNo}
</p>
<p>If you want to update your information</p>
<Button variant="primary" href="/update-profile">
Update Information
</Button>
</div>
) : (
<div className="mx-4">
<p>
You are not logged in. Please log in or register to
proceed.
</p>
<Button variant="primary" href="/register">
Register
</Button>
<Button className="mx-2" variant="primary" href="/login">
Login
</Button>
</div>
)}
</div>
</Collapse>
</div>
{/* Collapsible Table - Address */} {/* Collapsible Table - Address */}
<div className="border rounded mb-3 p-3"> <AddressCollapse
<h3> addressOpen={addressOpen}
<div className="d-flex justify-content-between align-items-start"> setAddressOpen={setAddressOpen}
<span className="fs-6">2. Address</span> isCustomerLoggedIn={isCustomerLoggedIn}
<Button
variant="link"
onClick={() => setAddressOpen(!addressOpen)}
aria-controls="addressCollapse"
aria-expanded={addressOpen}
>
<i
className={`bi bi-chevron-${addressOpen ? "up" : "down"}`}
></i>
</Button>
</div>
</h3>
<Collapse in={addressOpen}>
<div id="addressCollapse">
{isCustomerLoggedIn ? (
<Row>
<span className="mb-2" style={{ fontSize: "16px" }}>
The selected address will be used both as your personal
address (for invoice) and as your delivery address.
</span>
<Col className="border rounded mb-3 p-3" md={6}>
{/* Left column - Add New Address Form Dropdown */}
<div>
{addAddressFormOpen && (
<AddressForm
selectedAddress={selectedAddress} selectedAddress={selectedAddress}
setSelectedAddress={setSelectedAddress} setSelectedAddress={setSelectedAddress}
handleContinueAddress={handleContinueAddress} handleContinueAddress={handleContinueAddress}
addAddressFormOpen={addAddressFormOpen}
setAddAddressFormOpen={setAddAddressFormOpen}
addressFormError={addressFormError} addressFormError={addressFormError}
addresses={addresses}
handleDeleteAddress={handleDeleteAddress}
/> />
)}
<Button
className="my-3"
variant="primary"
onClick={() =>
setAddAddressFormOpen(!addAddressFormOpen)
}
>
Add New Address
</Button>
</div>
</Col>
<Col md={6}> {/* Collapsible Table - Shipping Method */}
{/* Right column - My Addresses from addressForm */} <ShippingMethodCollapse
{addresses && addresses.length > 0 ? ( shippingMethodOpen={shippingMethodOpen}
<Table striped bordered hover> setShippingMethodOpen={setShippingMethodOpen}
<thead> selectedShippingMethod={selectedShippingMethod}
<tr> setSelectedShippingMethod={setSelectedShippingMethod}
<th>My Addresses</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
{addresses.map((address, index) => (
<tr key={index}>
<td>
<div className="d-flex justify-content-between align-items-center">
<Form.Check
type="radio"
id={`addressRadio${index}`}
name="addressRadio"
checked={selectedAddressId === address.id}
onChange={() =>
handleAddressSelect(address.id)
}
/> />
<p className="mx-3">
{address.street}, {address.city},{" "}
{address.state}, {address.zipCode},{" "}
{address.country}
</p>
</div>
</td>
<td>
<Button
variant="link"
className="text-danger"
onClick={() =>
handleDeleteAddress(address._id)
}
>
<i className="bi bi-trash"></i>
</Button>
</td>
</tr>
))}
</tbody>
</Table>
) : (
<p>No existing addresses. Add a new address below.</p>
)}
</Col>
</Row>
) : (
<p>
User not logged in. Log in to view and manage addresses.
</p>
)}
</div>
</Collapse>
</div>
{/* Collapsible Table - Payment */} {/* Collapsible Table - Payment */}
<div className="border rounded mb-3 p-3"> <PaymentCollapse
<h3> totalPrice={totalPrice}
<div className="d-flex justify-content-between align-items-start"> userDetails={userDetails}
<span className="fs-6">4. Payment</span> selectedShippingMethod={selectedShippingMethod}
<Button cartItems={cartItems}
variant="link" appliedPromoCode={appliedPromoCode}
onClick={() => setPaymentOpen(!paymentOpen)} selectedAddress={selectedAddress}
aria-controls="paymentCollapse" />
aria-expanded={paymentOpen}
>
<i
className={`bi bi-chevron-${paymentOpen ? "up" : "down"}`}
></i>
</Button>
</div>
</h3>
<Collapse in={paymentOpen}>
<div id="paymentCollapse">
{/* Placeholder content for payment table */}
</div>
</Collapse>
</div>
</Col> </Col>
{/* Right side - Order Summary */} {/* Right side - Order Summary */}
<Col xs={12} md={4} className="text-start"> <OrderSummary
<h3>Order Summary</h3> cartItems={cartItems}
<Table striped bordered hover> shippingFee={shippingFee}
<tbody> totalPrice={totalPrice}
<tr> discountAmount={discountAmount}
<td>Total Items</td> promoCode={promoCode}
<td> setPromoCode={setPromoCode}
{cartItems.reduce((total, item) => total + item.quantity, 0)} handleApplyPromoCode={handleApplyPromoCode}
</td> appliedPromoCode={appliedPromoCode}
</tr>
<tr>
<td>Shipping Fee</td>
<td>{shippingFee.value}</td>
</tr>
<tr>
<td>Shipping Details</td>
<td>{shippingFee.details}</td>
</tr>
<tr>
<td>Total</td>
<td>
<strong>{totalPrice}</strong>
</td>
</tr>
<tr>
<td>Discount</td>
<td>
<strong>{discountAmount}%</strong>
</td>
</tr>
<tr>
<td colSpan="2">
<Form.Group controlId="promoCode">
<Form.Label>Promo Code</Form.Label>
<Form.Control
type="text"
placeholder="Enter promo code"
value={promoCode}
onChange={(e) => setPromoCode(e.target.value)}
/> />
<Button
className="mt-3"
variant="primary"
onClick={handleApplyPromoCode}
>
Apply Promo Code
</Button>
</Form.Group>
{appliedPromoCode && (
<div>
<p className="mt-2">
Applied Promo Code: <strong>{appliedPromoCode}</strong>
</p>
</div>
)}
</td>
</tr>
</tbody>
</Table>
</Col>
</Row> </Row>
<ToastContainer position="bottom-right" /> <ToastContainer position="bottom-right" />
</Container> </Container>

@ -0,0 +1,33 @@
// OrderConfirmationPage.js
import React from "react";
import OrderDetails from "../components/OrderDetails";
import { Container } from "react-bootstrap";
const OrderConfirmationPage = ({ location }) => {
// Check if location is undefined
if (!location || !location.state) {
return (
<div>
<Container>
<h2>Order Confirmation</h2>
<p>Error: No order data found.</p>
</Container>
</div>
);
}
// Retrieve orderData from location.state
const orderData = location.state.orderData;
return (
<div>
<Container>
<h2>Order Confirmation</h2>
{/* Use the OrderDetails component to display order details */}
<OrderDetails orderData={orderData} />
</Container>
</div>
);
};
export default OrderConfirmationPage;
Loading…
Cancel
Save