Capstone 3 Added

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

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

@ -532,4 +532,3 @@ exports.updateAddress = async (req, res) => {
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 ProductPage from './pages/ProductPage';
import CheckoutPage from './pages/CheckoutPage';
import OrderConfirmationPage from './pages/OrderConfirmationPage';
import Error from './pages/Error'; // Import the NotFound component
import { ToastContainer } from 'react-toastify';
import 'react-toastify/dist/ReactToastify.css';
@ -108,6 +109,7 @@ function App() {
<Route path="/404" element={<Error />} />
<Route path="/product/:productId" element={<ProductPage />} />
<Route path="/checkout" element={<CheckoutPage />} />
<Route path="/order-confirmation" element={<OrderConfirmationPage />} />
{/* Catch-all route that redirects to the home page */}
<Route path="*" element={<Navigate to="/404" />} />
</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 { Form, Button } from 'react-bootstrap';
import React from "react";
import { Form, Button } from "react-bootstrap";
const AddressForm = ({
selectedAddress,
@ -8,91 +7,92 @@ const AddressForm = ({
handleContinueAddress,
addressFormError,
}) => {
const philippineStates = [
"...",
"Abra",
"Agusan del Norte",
"Agusan del Sur",
"Aklan",
"Albay",
"Antique",
"Apayao",
"Aurora",
"Basilan",
"Bataan",
"Batanes",
"Batangas",
"Benguet",
"Biliran",
"Bohol",
"Bukidnon",
"Bulacan",
"Cagayan",
"Camarines Norte",
"Camarines Sur",
"Camiguin",
"Capiz",
"Catanduanes",
"Cavite",
"Cebu",
"Cotabato",
"Davao de Oro (formerly Compostela Valley)",
"Davao del Norte",
"Davao del Sur",
"Davao Occidental",
"Davao Oriental",
"Dinagat Islands",
"Eastern Samar",
"Guimaras",
"Ifugao",
"Ilocos Norte",
"Ilocos Sur",
"Iloilo",
"Isabela",
"Kalinga",
"La Union",
"Laguna",
"Lanao del Norte",
"Lanao del Sur",
"Leyte",
"Maguindanao",
"Marinduque",
"Masbate",
"Misamis Occidental",
"Misamis Oriental",
"Mountain Province",
"Negros Occidental",
"Negros Oriental",
"Northern Samar",
"Nueva Ecija",
"Nueva Vizcaya",
"Occidental Mindoro",
"Oriental Mindoro",
"Palawan",
"Pampanga",
"Pangasinan",
"Quezon",
"Quirino",
"Rizal",
"Romblon",
"Samar (Western Samar)",
"Sarangani",
"Siquijor",
"Sorsogon",
"South Cotabato",
"Southern Leyte",
"Sultan Kudarat",
"Sulu",
"Surigao del Norte",
"Surigao del Sur",
"Tarlac",
"Tawi-Tawi",
"Zambales",
"Zamboanga del Norte",
"Zamboanga del Sur",
"Zamboanga Sibugay",
];
// Define the array of Philippine states
const philippineStates = [
"...",
"Abra",
"Agusan del Norte",
"Agusan del Sur",
"Aklan",
"Albay",
"Antique",
"Apayao",
"Aurora",
"Basilan",
"Bataan",
"Batanes",
"Batangas",
"Benguet",
"Biliran",
"Bohol",
"Bukidnon",
"Bulacan",
"Cagayan",
"Camarines Norte",
"Camarines Sur",
"Camiguin",
"Capiz",
"Catanduanes",
"Cavite",
"Cebu",
"Cotabato",
"Davao de Oro (formerly Compostela Valley)",
"Davao del Norte",
"Davao del Sur",
"Davao Occidental",
"Davao Oriental",
"Dinagat Islands",
"Eastern Samar",
"Guimaras",
"Ifugao",
"Ilocos Norte",
"Ilocos Sur",
"Iloilo",
"Isabela",
"Kalinga",
"La Union",
"Laguna",
"Lanao del Norte",
"Lanao del Sur",
"Leyte",
"Maguindanao",
"Marinduque",
"Masbate",
"Misamis Occidental",
"Misamis Oriental",
"Mountain Province",
"Negros Occidental",
"Negros Oriental",
"Northern Samar",
"Nueva Ecija",
"Nueva Vizcaya",
"Occidental Mindoro",
"Oriental Mindoro",
"Palawan",
"Pampanga",
"Pangasinan",
"Quezon",
"Quirino",
"Rizal",
"Romblon",
"Samar (Western Samar)",
"Sarangani",
"Siquijor",
"Sorsogon",
"South Cotabato",
"Southern Leyte",
"Sultan Kudarat",
"Sulu",
"Surigao del Norte",
"Surigao del Sur",
"Tarlac",
"Tawi-Tawi",
"Zambales",
"Zamboanga del Norte",
"Zamboanga del Sur",
"Zamboanga Sibugay",
];
const countryStates = ["...", "Philippines"];
return (
<Form onSubmit={handleContinueAddress}>
@ -142,6 +142,7 @@ const AddressForm = ({
required
/>
</Form.Group>
<Form.Group controlId="state">
<Form.Label>State</Form.Label>
<Form.Control
@ -160,9 +161,27 @@ const AddressForm = ({
))}
</Form.Control>
</Form.Group>
<Form.Group controlId="country">
<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>
<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 [editedAddress, setEditedAddress] = useState({ ...address });
const [updateStatus, setUpdateStatus] = useState(null);
const [setUpdateStatus] = useState(null);
useEffect(() => {
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,
Row,
Col,
Table,
Button,
Collapse,
Form,
Modal,
} from "react-bootstrap";
import AddressForm from "../components/AddressForm";
import EditAddress from "../components/EditAddress";
import OrderSummary from "../components/OrderSummary";
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 { toast, ToastContainer } from "react-toastify";
import "react-toastify/dist/ReactToastify.css";
@ -18,14 +16,14 @@ import "react-toastify/dist/ReactToastify.css";
const CheckoutPage = () => {
const [cartItems, setCartItems] = useState([]);
const [totalPrice, setTotalPrice] = useState(0);
const [shippingFee, setShippingFee] = useState({
const [shippingFee] = useState({
value: 200,
details: "Fixed shipping fee of ₱200",
});
const [promoCode, setPromoCode] = useState("");
const [personalInfoOpen, setPersonalInfoOpen] = useState(true);
const [addressOpen, setAddressOpen] = useState(false);
const [paymentOpen, setPaymentOpen] = useState(false);
const [shippingMethodOpen, setShippingMethodOpen] = useState(false);
const [isCustomerLoggedIn, setIsCustomerLoggedIn] = useState(false);
const [userDetails, setUserDetails] = useState(null);
const [appliedPromoCode, setAppliedPromoCode] = useState(null);
@ -33,13 +31,9 @@ const CheckoutPage = () => {
const [discountAmount, setDiscountAmount] = useState(0);
const [addAddressFormOpen, setAddAddressFormOpen] = useState(false);
const [addresses, setAddresses] = useState([]);
const [selectedAddressId, setSelectedAddressId] = useState(null);
const [addressFormError, setAddressFormError] = useState(null);
const handleAddressSelect = (addressId) => {
setSelectedAddressId(addressId);
};
const [addressFormError] = useState(null);
const [selectedAddress, setSelectedAddress] = useState(null);
const [selectedShippingMethod, setSelectedShippingMethod] = useState(null);
const handleDeleteAddress = async (addressId) => {
try {
@ -91,15 +85,6 @@ const CheckoutPage = () => {
}
};
const [selectedAddress, setSelectedAddress] = useState({
street: "",
zipCode: "",
city: "",
state: "",
country: "Philippines",
});
const handleContinueAddress = async (e) => {
e.preventDefault(); // Prevent the default form submission behavior
@ -108,7 +93,8 @@ const CheckoutPage = () => {
!selectedAddress.street ||
!selectedAddress.zipCode ||
!selectedAddress.city ||
!selectedAddress.state
!selectedAddress.state ||
!selectedAddress.country
) {
console.error("Please fill in all required address fields");
// Display an error toast
@ -211,11 +197,6 @@ const CheckoutPage = () => {
const data = await response.json();
setAddresses(data);
// Preselect the first address if available
if (data.length > 0) {
handleAddressSelect(data[0].id);
}
return data; // Return the fetched addresses
} else {
console.error("Failed to fetch addresses:", response.status);
@ -455,6 +436,9 @@ const CheckoutPage = () => {
fetchAddresses();
}, [isCustomerLoggedIn]);
return (
<Container className="my-5 text-start">
<h2>Checkout</h2>
@ -462,256 +446,58 @@ const CheckoutPage = () => {
{/* Left side - Collapsible Forms */}
<Col xs={12} md={8}>
{/* Collapsible Table - Personal Information */}
<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>
<PersonalInfoCollapse
personalInfoOpen={personalInfoOpen}
setPersonalInfoOpen={setPersonalInfoOpen}
isCustomerLoggedIn={isCustomerLoggedIn}
userDetails={userDetails}
/>
{/* Collapsible Table - Address */}
<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={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>
<AddressCollapse
addressOpen={addressOpen}
setAddressOpen={setAddressOpen}
isCustomerLoggedIn={isCustomerLoggedIn}
selectedAddress={selectedAddress}
setSelectedAddress={setSelectedAddress}
handleContinueAddress={handleContinueAddress}
addAddressFormOpen={addAddressFormOpen}
setAddAddressFormOpen={setAddAddressFormOpen}
addressFormError={addressFormError}
addresses={addresses}
handleDeleteAddress={handleDeleteAddress}
/>
{/* Collapsible Table - Shipping Method */}
<ShippingMethodCollapse
shippingMethodOpen={shippingMethodOpen}
setShippingMethodOpen={setShippingMethodOpen}
selectedShippingMethod={selectedShippingMethod}
setSelectedShippingMethod={setSelectedShippingMethod}
/>
{/* Collapsible Table - Payment */}
<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">
{/* Placeholder content for payment table */}
</div>
</Collapse>
</div>
<PaymentCollapse
totalPrice={totalPrice}
userDetails={userDetails}
selectedShippingMethod={selectedShippingMethod}
cartItems={cartItems}
appliedPromoCode={appliedPromoCode}
selectedAddress={selectedAddress}
/>
</Col>
{/* Right side - Order Summary */}
<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>{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>
<OrderSummary
cartItems={cartItems}
shippingFee={shippingFee}
totalPrice={totalPrice}
discountAmount={discountAmount}
promoCode={promoCode}
setPromoCode={setPromoCode}
handleApplyPromoCode={handleApplyPromoCode}
appliedPromoCode={appliedPromoCode}
/>
</Row>
<ToastContainer position="bottom-right" />
</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