created checkout section on checkout page

This commit is contained in:
Juthatip McDevitt 2024-05-24 13:49:07 -05:00
parent 7279157625
commit 0b6516e19d
6 changed files with 109 additions and 42 deletions

View file

@ -1,16 +1,39 @@
"use client"
import React, { useContext } from 'react'
import React, { useContext, useEffect, useState } from 'react'
import { CartContext, productTotal } from '../../components/AppContext'
import Image from 'next/image';
import { IoMdCloseCircleOutline } from "react-icons/io";
import AddressInfo from '../../components/layout/AddressInfo';
import useProfile from '../../components/UseProfile';
const CartPage = () => {
const {cartProducts, removeCartProduct} = useContext(CartContext);
const [address, setAddress] = useState({})
const {data:profileCheckoutData} = useProfile()
useEffect(() => {
if(profileCheckoutData?.city){
const {phoneNumber, streetAddress, city, stateProvince, zipCode, country} = profileCheckoutData;
const addressFormProfile = {phoneNumber, streetAddress, city, stateProvince, zipCode, country}
setAddress(addressFormProfile)
}
}, [profileCheckoutData])
function handleAddresschange(propName, value){
setAddress(prevAddress => ({...prevAddress, [propName]:value}));
}
//checkout to Stripe
function handleCheckout(ev){
fetch('/api/checkout', {
})
}
//cart total
let total = 0
let subtotal = 0
for(const p of cartProducts){
total += productTotal(p)
subtotal += productTotal(p)
}
@ -18,7 +41,7 @@ const CartPage = () => {
<div className='pb-20 md:pb-40'>
<div className='px-5 md:px-10 py-5 sm:py-10'>
<p className='text-center text-2xl sm:text-3xl md:text-4xl text-pink-500 uppercase font-bold drop-shadow-[0_2px_2px_rgba(105,105,105,1)]'>My cart</p>
<div className='grid grid-cols-1 md:grid-cols-2 gap-5 lg:gap-10 xl:gap-20 mt-5 md:mt-10'>
<div className='grid grid-cols-1 md:grid-cols-2 gap-10 mt-5 md:mt-10'>
<div className='border-t'>
{cartProducts?.length === 0 && (
<div>Your cart is empty</div>
@ -50,12 +73,33 @@ const CartPage = () => {
</div>
</div>
))}
<div className='hidden md:flex justify-end items-end mt-5 md:mt-10'>
<p className='uppercase font-semibold text-sm'>Sub-total <span className='ml-5'>${total}</span></p>
<div className='flex justify-end'>
<div className='flex flex-col gap-1 mt-5 md:mt-10 text-xs sm:text-sm'>
<div className='flex gap-10 justify-between font-semibold'>
<p className='uppercase'>subtotal</p>
<p className='ml-5 text-end'>${subtotal}</p>
</div>
<div className='flex gap-10 justify-between font-semibold'>
<p className='uppercase'>Delivery Fees</p>
<p className='ml-5 text-end'>$4.99</p>
</div>
<div className='flex gap-10 justify-between font-semibold'>
<p className='uppercase'>Order total</p>
<p className='ml-5 text-end'>${subtotal + 5}</p>
</div>
</div>
</div>
</div>
<div className='flex flex-col'>
<p className='uppercase text-gray-700 font-semibold'>Summary</p>
<div>
<div className='w-full md:max-w-lg mx-auto px-5 py-10 border rounded-md shadow-lg'>
<div className='flex flex-col w-full md:max-w-md mx-auto capitalize text-sm sm:text-base'>
<p className='uppercase font-semibold mb-5 text-center'>Shipping Address</p>
<form onSubmit={handleCheckout} className='border-t py-5'>
<AddressInfo addressProps={address} setAddressProps={handleAddresschange}/>
<button type='button' className='w-full rounded-md bg-pink-500 text-white hover:opacity-80 duration-300 p-2 block'>Process to pay</button>
</form>
</div>
</div>
</div>
</div>
</div>

View file

@ -1,11 +1,9 @@
"use client"
import { useSession } from 'next-auth/react'
import Image from 'next/image';
import { redirect } from 'next/navigation';
import React, { useEffect, useState } from 'react'
import toast from 'react-hot-toast';
import UserTabs from "../../components/layout/UserTab"
import EditImage from "../../components/layout/EditImage"
import UserForm from "../../components/layout/UserForm"
const ProfilePage = () => {
@ -14,7 +12,7 @@ const ProfilePage = () => {
const {status} = session;
const [user, setUser] = useState(null);
const [isAdmin, setIsAdmin] = useState(false);
const [profileFetchd, setProfileFetched] = useState(false);
const [profileFetched, setProfileFetched] = useState(false);
useEffect(() => {
@ -37,7 +35,7 @@ const ProfilePage = () => {
const response = await fetch('/api/profile', {
method: 'PUT',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({data}),
body: JSON.stringify(data),
});
if(response.ok)
resolve()
@ -52,7 +50,7 @@ const ProfilePage = () => {
}
if(status === 'loading' || !profileFetchd){
if(status === 'loading' || !profileFetched){
return <p className='flex justify-center items-center'>Loading...</p>
}
if(status === 'unauthenticated'){

View file

@ -7,13 +7,13 @@ import Link from 'next/link';
const Userspage = () => {
const {data, loading} = useProfile();
const [users, setUser] = useState([]);
const [users, setUsers] = useState([]);
useEffect(() => {
fetch('api/users').then(response => {
fetch('/api/users').then(response => {
response.json().then(users => {
setUser(users)
setUsers(users)
})
})
}, [])

View file

@ -0,0 +1,38 @@
import React from 'react'
const AddressInfo = ({addressProps, setAddressProps}) => {
const {phoneNumber, streetAddress, city, stateProvince, zipCode, country} = addressProps;
return (
<>
<div>
<label className='text-gray-700 text-sm font-semibold'>Phone number</label>
<input type='tel' placeholder='Phone number' value={phoneNumber} onChange={ev => setAddressProps('phoneNumber', ev.target.value)}/>
</div>
<div>
<label className='text-gray-700 text-sm font-semibold'>Street Address</label>
<input type='text' placeholder='Address' value={streetAddress} onChange={ev => setAddressProps('streetAddress', ev.target.value)}/>
</div>
<div>
<label className='text-gray-700 text-sm font-semibold'>City</label>
<input type='text' placeholder='City' value={city} onChange={ev => setAddressProps('city', ev.target.value)}/>
</div>
<div className='flex flex-col sm:flex-row gap-0 sm:gap-2'>
<div>
<label className='text-gray-700 text-sm font-semibold'>State/Province</label>
<input type='text' placeholder='State/Province' value={stateProvince} onChange={ev => setAddressProps('stateProvince', ev.target.value)}/>
</div>
<div>
<label className='text-gray-700 text-sm font-semibold'>Zip/Postal code</label>
<input type='text' placeholder='Zip/Postal code' value={zipCode} onChange={ev => setAddressProps('zipCode', ev.target.value)}/>
</div>
</div>
<div>
<label className='text-gray-700 text-sm font-semibold'>Country</label>
<input type='text' placeholder='Country' value={country} onChange={ev => setAddressProps('country', ev.target.value)}/>
</div>
</>
)
}
export default AddressInfo

View file

@ -65,6 +65,7 @@ const Header = () => {
)}
{status === 'unauthenticated' && (
<>
<Link href='/cart' className="flex gap-1 items-center"><BsBox2Heart/>({cartProducts.length})</Link>
<Link href='/login' className="">Login</Link>
<Link href='/register' className="px-4 py-2 border border-[#DCA0AE] rounded-full hover:bg-[#DCA0AE] hover:text-white duration-300">Register</Link>
</>

View file

@ -2,6 +2,7 @@
import React, { useState } from 'react'
import EditImage from '../../components/layout/EditImage'
import useProfile from '../UseProfile';
import AddressInfo from '../layout/AddressInfo';
const UserForm = ({user, onSave}) => {
const [userName, setUserName] = useState(user?.name || '');
@ -16,6 +17,16 @@ const UserForm = ({user, onSave}) => {
const {data:loginAsUser} = useProfile();
function handleAddressChange(propName, value){
if(propName === 'phoneNumber') setPhoneNumber(value);
if(propName === 'streetAddress') setStreetAddress(value);
if(propName === 'city') setCity(value);
if(propName === 'stateProvince') setStateProvince(value);
if(propName === 'zipCode') setZipCode(value);
if(propName === 'country') setCountry(value);
}
return (
<div className='max-w-md mx-auto mt-10'>
<div className='flex flex-col gap-2 justify-center items-center max-w-[100px] mx-auto'>
@ -30,32 +41,7 @@ const UserForm = ({user, onSave}) => {
<label className='text-gray-700 text-sm font-semibold'>Email</label>
<input type='text' value={user.email} disabled={true} className='disabled:bg-gray-200 disabled:text-gray-400'/>
</div>
<div>
<label className='text-gray-700 text-sm font-semibold'>Phone number</label>
<input type='tel' placeholder='Phone number' value={phoneNumber} onChange={ev => setPhoneNumber(ev.target.value)}/>
</div>
<div>
<label className='text-gray-700 text-sm font-semibold'>Street Address</label>
<input type='text' placeholder='Address' value={streetAddress} onChange={ev => setStreetAddress(ev.target.value)}/>
</div>
<div>
<label className='text-gray-700 text-sm font-semibold'>City</label>
<input type='text' placeholder='City' value={city} onChange={ev => setCity(ev.target.value)}/>
</div>
<div className='flex flex-col sm:flex-row gap-0 sm:gap-2'>
<div>
<label className='text-gray-700 text-sm font-semibold'>State/Province</label>
<input type='text' placeholder='State/Province' value={stateProvince} onChange={ev => setStateProvince(ev.target.value)}/>
</div>
<div>
<label className='text-gray-700 text-sm font-semibold'>Zip/Postal code</label>
<input type='text' placeholder='Zip/Postal code' value={zipCode} onChange={ev => setZipCode(ev.target.value)}/>
</div>
</div>
<div>
<label className='text-gray-700 text-sm font-semibold'>Country</label>
<input type='text' placeholder='Country' value={country} onChange={ev => setCountry(ev.target.value)}/>
</div>
<AddressInfo addressProps={{phoneNumber, streetAddress, city, stateProvince, zipCode, country}} setAddressProps={handleAddressChange}/>
{loginAsUser.admin && (
<div className='flex gap-2 items-center mb-5'>
<label htmlFor='admin' className='text-gray-700 text-sm font-semibold'>Admin:</label>