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" "use client"
import React, { useContext } from 'react' import React, { useContext, useEffect, useState } from 'react'
import { CartContext, productTotal } from '../../components/AppContext' import { CartContext, productTotal } from '../../components/AppContext'
import Image from 'next/image'; import Image from 'next/image';
import { IoMdCloseCircleOutline } from "react-icons/io"; import { IoMdCloseCircleOutline } from "react-icons/io";
import AddressInfo from '../../components/layout/AddressInfo';
import useProfile from '../../components/UseProfile';
const CartPage = () => { const CartPage = () => {
const {cartProducts, removeCartProduct} = useContext(CartContext); 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 //cart total
let total = 0 let subtotal = 0
for(const p of cartProducts){ 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='pb-20 md:pb-40'>
<div className='px-5 md:px-10 py-5 sm:py-10'> <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> <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'> <div className='border-t'>
{cartProducts?.length === 0 && ( {cartProducts?.length === 0 && (
<div>Your cart is empty</div> <div>Your cart is empty</div>
@ -50,12 +73,33 @@ const CartPage = () => {
</div> </div>
</div> </div>
))} ))}
<div className='hidden md:flex justify-end items-end mt-5 md:mt-10'> <div className='flex justify-end'>
<p className='uppercase font-semibold text-sm'>Sub-total <span className='ml-5'>${total}</span></p> <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> </div>
<div className='flex flex-col'> <div>
<p className='uppercase text-gray-700 font-semibold'>Summary</p> <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> </div>
</div> </div>

View file

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

View file

@ -7,13 +7,13 @@ import Link from 'next/link';
const Userspage = () => { const Userspage = () => {
const {data, loading} = useProfile(); const {data, loading} = useProfile();
const [users, setUser] = useState([]); const [users, setUsers] = useState([]);
useEffect(() => { useEffect(() => {
fetch('api/users').then(response => { fetch('/api/users').then(response => {
response.json().then(users => { 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' && ( {status === 'unauthenticated' && (
<> <>
<Link href='/cart' className="flex gap-1 items-center"><BsBox2Heart/>({cartProducts.length})</Link>
<Link href='/login' className="">Login</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> <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 React, { useState } from 'react'
import EditImage from '../../components/layout/EditImage' import EditImage from '../../components/layout/EditImage'
import useProfile from '../UseProfile'; import useProfile from '../UseProfile';
import AddressInfo from '../layout/AddressInfo';
const UserForm = ({user, onSave}) => { const UserForm = ({user, onSave}) => {
const [userName, setUserName] = useState(user?.name || ''); const [userName, setUserName] = useState(user?.name || '');
@ -16,6 +17,16 @@ const UserForm = ({user, onSave}) => {
const {data:loginAsUser} = useProfile(); 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 ( return (
<div className='max-w-md mx-auto mt-10'> <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'> <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> <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'/> <input type='text' value={user.email} disabled={true} className='disabled:bg-gray-200 disabled:text-gray-400'/>
</div> </div>
<div> <AddressInfo addressProps={{phoneNumber, streetAddress, city, stateProvince, zipCode, country}} setAddressProps={handleAddressChange}/>
<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>
{loginAsUser.admin && ( {loginAsUser.admin && (
<div className='flex gap-2 items-center mb-5'> <div className='flex gap-2 items-center mb-5'>
<label htmlFor='admin' className='text-gray-700 text-sm font-semibold'>Admin:</label> <label htmlFor='admin' className='text-gray-700 text-sm font-semibold'>Admin:</label>