Image tags are fucked for right now.
Cart is working
This commit is contained in:
164
src/providers/cart.tsx
Normal file
164
src/providers/cart.tsx
Normal file
@ -0,0 +1,164 @@
|
||||
'use client'; // include with Next.js 13+
|
||||
|
||||
import { HttpTypes } from '@medusajs/types';
|
||||
import React, { createContext, useContext, useEffect, useState } from 'react';
|
||||
|
||||
import { useRegion } from './region';
|
||||
|
||||
type CartContextType = {
|
||||
cart?: HttpTypes.StoreCart;
|
||||
setCart: React.Dispatch<React.SetStateAction<HttpTypes.StoreCart | undefined>>;
|
||||
addToCart: (variant_id: string, quantity: number) => void;
|
||||
updateQuantity: (item_id: string, quantity: number) => void;
|
||||
removeItem: (item_id: string) => void;
|
||||
refreshCart: () => void;
|
||||
};
|
||||
|
||||
const CartContext = createContext<CartContextType | null>(null);
|
||||
|
||||
type CartProviderProps = {
|
||||
children: React.ReactNode;
|
||||
};
|
||||
|
||||
export const CartProvider = ({ children }: CartProviderProps) => {
|
||||
const [cart, setCart] = useState<HttpTypes.StoreCart>();
|
||||
const { region } = useRegion();
|
||||
|
||||
useEffect(() => {
|
||||
if (cart || !region) {
|
||||
return;
|
||||
}
|
||||
|
||||
const cartId = localStorage.getItem('cart_id');
|
||||
if (!cartId) {
|
||||
// create a cart
|
||||
fetch(`${process.env.NEXT_PUBLIC_BASE_URL}/store/carts`, {
|
||||
method: 'POST',
|
||||
credentials: 'include',
|
||||
headers: {
|
||||
'x-publishable-api-key': process.env.NEXT_PUBLIC_MEDUSA_PUBLISHABLE_KEY as string,
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
region_id: region.id,
|
||||
}),
|
||||
})
|
||||
.then((res) => res.json())
|
||||
.then(({ cart: dataCart }) => {
|
||||
localStorage.setItem('cart_id', dataCart.id);
|
||||
setCart(dataCart);
|
||||
});
|
||||
} else {
|
||||
// retrieve cart
|
||||
fetch(`${process.env.NEXT_PUBLIC_BASE_URL}/store/carts/${cartId}`, {
|
||||
credentials: 'include',
|
||||
headers: {
|
||||
'x-publishable-api-key': process.env.NEXT_PUBLIC_MEDUSA_PUBLISHABLE_KEY as string,
|
||||
},
|
||||
})
|
||||
.then((res) => res.json())
|
||||
.then(({ cart: dataCart }) => {
|
||||
setCart(dataCart);
|
||||
});
|
||||
}
|
||||
}, [cart, region]);
|
||||
|
||||
const refreshCart = () => {
|
||||
localStorage.removeItem('cart_id');
|
||||
setCart(undefined);
|
||||
};
|
||||
|
||||
function addToCart(variant_id: string, quantity: number = 1) {
|
||||
if (!cart) {
|
||||
return;
|
||||
}
|
||||
|
||||
fetch(`http://localhost:9000/store/carts/${cart.id}/line-items`, {
|
||||
credentials: 'include',
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'x-publishable-api-key': process.env.NEXT_PUBLIC_MEDUSA_PUBLISHABLE_KEY as string,
|
||||
},
|
||||
body: JSON.stringify({
|
||||
variant_id,
|
||||
quantity: quantity,
|
||||
}),
|
||||
})
|
||||
.then((res) => res.json())
|
||||
.then(({ cart }) => {
|
||||
// use cart
|
||||
console.log('Product added to cart: ', cart);
|
||||
setCart(cart);
|
||||
});
|
||||
}
|
||||
|
||||
function updateQuantity(item_id: string, quantity: number) {
|
||||
if (!cart) {
|
||||
return;
|
||||
}
|
||||
|
||||
fetch(`${process.env.NEXT_PUBLIC_BASE_URL}/store/carts/${cart.id}/line-items/${item_id}`, {
|
||||
credentials: 'include',
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'x-publishable-api-key': process.env.NEXT_PUBLIC_MEDUSA_PUBLISHABLE_KEY as string,
|
||||
},
|
||||
body: JSON.stringify({
|
||||
quantity,
|
||||
}),
|
||||
})
|
||||
.then((res) => res.json())
|
||||
.then(({ cart }) => {
|
||||
// use cart
|
||||
console.log('Quantity in cart updated: ', cart);
|
||||
setCart(cart);
|
||||
});
|
||||
}
|
||||
|
||||
function removeItem(item_id: string) {
|
||||
if (!cart) {
|
||||
return;
|
||||
}
|
||||
|
||||
fetch(`${process.env.NEXT_PUBLIC_BASE_URL}/store/carts/${cart.id}/line-items/${item_id}`, {
|
||||
credentials: 'include',
|
||||
headers: {
|
||||
'x-publishable-api-key': process.env.NEXT_PUBLIC_MEDUSA_PUBLISHABLE_KEY as string,
|
||||
},
|
||||
method: 'DELETE',
|
||||
})
|
||||
.then((res) => res.json())
|
||||
.then(({ parent: cart }) => {
|
||||
// use cart
|
||||
console.log('Item removed from cart: ', cart);
|
||||
setCart(cart);
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<CartContext.Provider
|
||||
value={{
|
||||
cart,
|
||||
setCart,
|
||||
addToCart,
|
||||
updateQuantity,
|
||||
removeItem,
|
||||
refreshCart,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</CartContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
export const useCart = () => {
|
||||
const context = useContext(CartContext);
|
||||
|
||||
if (!context) {
|
||||
throw new Error('useCart must be used within a CartProvider');
|
||||
}
|
||||
|
||||
return context;
|
||||
};
|
||||
245
src/providers/customer.tsx
Normal file
245
src/providers/customer.tsx
Normal file
@ -0,0 +1,245 @@
|
||||
'use client'; // include with Next.js 13+
|
||||
|
||||
import { HttpTypes } from '@medusajs/types';
|
||||
import React, { createContext, useContext, useEffect, useState } from 'react';
|
||||
|
||||
type CustomerContextType = {
|
||||
customer: HttpTypes.StoreCustomer | undefined;
|
||||
register: (firstName: string, lastName: string, email: string, password: string) => Promise<void>;
|
||||
login: (email: string, password: string) => Promise<void>;
|
||||
logout: () => void;
|
||||
};
|
||||
|
||||
const CustomerContext = createContext<CustomerContextType | null>(null);
|
||||
|
||||
type CustomerProviderProps = {
|
||||
children: React.ReactNode;
|
||||
};
|
||||
|
||||
export const CustomerProvider = ({ children }: CustomerProviderProps) => {
|
||||
const [customer, setCustomer] = useState<HttpTypes.StoreCustomer | undefined>();
|
||||
const [jwt, setJwt] = useState<string | null>();
|
||||
|
||||
useEffect(() => {
|
||||
const storedToken = localStorage.getItem('jwt');
|
||||
const storedCustomer = localStorage.getItem('user');
|
||||
|
||||
console.log('Stored customer: ', storedCustomer);
|
||||
if (storedToken) {
|
||||
setJwt(storedToken);
|
||||
}
|
||||
|
||||
if (storedCustomer) {
|
||||
setCustomer(JSON.parse(storedCustomer));
|
||||
}
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (customer) {
|
||||
localStorage.setItem('user', JSON.stringify(customer));
|
||||
console.log('User set to: ', customer);
|
||||
} else {
|
||||
localStorage.removeItem('user');
|
||||
console.log('User removed');
|
||||
}
|
||||
}, [customer]);
|
||||
|
||||
useEffect(() => {
|
||||
if (jwt) {
|
||||
localStorage.setItem('jwt', jwt);
|
||||
console.log('Jwt set to: ', jwt);
|
||||
} else {
|
||||
localStorage.removeItem('jwt');
|
||||
console.log('Jwt removed');
|
||||
}
|
||||
}, [jwt]);
|
||||
|
||||
const login = async (email: string, password: string) => {
|
||||
try {
|
||||
// Handle form submission here
|
||||
if (!email) {
|
||||
throw new Error('Please enter email id');
|
||||
}
|
||||
|
||||
if (!password) {
|
||||
throw new Error('Please enter password');
|
||||
}
|
||||
|
||||
// obtain JWT token
|
||||
await getJwt(email, password);
|
||||
|
||||
// get customer
|
||||
await getCustomer();
|
||||
|
||||
console.log('Logged in');
|
||||
} catch (error) {
|
||||
console.error('Login error:', error);
|
||||
// Optionally, handle error (e.g., show error message to user)
|
||||
throw error; // Re-throw to allow caller to handle it
|
||||
}
|
||||
};
|
||||
const register = async (firstName: string, lastName: string, email: string, password: string) => {
|
||||
try {
|
||||
// Handle form submission here
|
||||
if (!firstName) {
|
||||
throw new Error('Please enter first name');
|
||||
}
|
||||
|
||||
if (!lastName) {
|
||||
throw new Error('Please enter last name');
|
||||
}
|
||||
|
||||
if (!email) {
|
||||
throw new Error('Please enter email id');
|
||||
}
|
||||
|
||||
if (!password) {
|
||||
throw new Error('Please enter password');
|
||||
}
|
||||
|
||||
// obtain JWT token
|
||||
await registerEmail(email, password);
|
||||
|
||||
// get customer
|
||||
await createCustomer(firstName, lastName, email);
|
||||
|
||||
console.log('Logged in');
|
||||
} catch (error) {
|
||||
console.error('Login error:', error);
|
||||
// Optionally, handle error (e.g., show error message to user)
|
||||
throw error; // Re-throw to allow caller to handle it
|
||||
}
|
||||
};
|
||||
const logout = async () => {
|
||||
try {
|
||||
setJwt(null);
|
||||
setCustomer(undefined);
|
||||
} catch (error) {
|
||||
console.error('Login error:', error);
|
||||
// Optionally, handle error (e.g., show error message to user)
|
||||
throw error; // Re-throw to allow caller to handle it
|
||||
}
|
||||
};
|
||||
const getJwt = async (email: string, password: string) => {
|
||||
try {
|
||||
const { token } = await fetch(`${process.env.NEXT_PUBLIC_BASE_URL}/auth/customer/emailpass`, {
|
||||
credentials: 'include',
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'x-publishable-api-key': process.env.NEXT_PUBLIC_MEDUSA_PUBLISHABLE_KEY as string,
|
||||
},
|
||||
body: JSON.stringify({
|
||||
email,
|
||||
password,
|
||||
}),
|
||||
}).then((res) => res.json());
|
||||
|
||||
if (!token) {
|
||||
throw new Error('Unable to retrieve token');
|
||||
}
|
||||
setJwt(token);
|
||||
} catch (error) {
|
||||
console.error('Login error in getJwt: ', error);
|
||||
// Optionally, handle error (e.g., show error message to user)
|
||||
throw error; // Re-throw to allow caller to handle it
|
||||
}
|
||||
};
|
||||
const registerEmail = async (email: string, password: string) => {
|
||||
try {
|
||||
const { token } = await fetch(`${process.env.NEXT_PUBLIC_BASE_URL}/auth/customer/emailpass/register`, {
|
||||
credentials: 'include',
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'x-publishable-api-key': process.env.NEXT_PUBLIC_MEDUSA_PUBLISHABLE_KEY as string,
|
||||
},
|
||||
body: JSON.stringify({
|
||||
email,
|
||||
password,
|
||||
}),
|
||||
}).then((res) => res.json());
|
||||
|
||||
if (!token) {
|
||||
throw new Error('Unable to retrieve token');
|
||||
}
|
||||
setJwt(token);
|
||||
} catch (error) {
|
||||
console.error('Register error in registerEmail: ', error);
|
||||
// Optionally, handle error (e.g., show error message to user)
|
||||
throw error; // Re-throw to allow caller to handle it
|
||||
}
|
||||
};
|
||||
const getCustomer = async () => {
|
||||
try {
|
||||
if (!jwt || customer) {
|
||||
return;
|
||||
}
|
||||
// get customer
|
||||
const response = await fetch(`${process.env.NEXT_PUBLIC_BASE_URL}/store/customers/me`, {
|
||||
credentials: 'include',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: `Bearer ${jwt}`,
|
||||
'x-publishable-api-key': process.env.NEXT_PUBLIC_MEDUSA_PUBLISHABLE_KEY as string,
|
||||
},
|
||||
}).then((res) => res.json());
|
||||
|
||||
setCustomer(response.customer);
|
||||
} catch (error) {
|
||||
console.error('Login error:', error);
|
||||
// Optionally, handle error (e.g., show error message to user)
|
||||
throw error; // Re-throw to allow caller to handle it
|
||||
}
|
||||
};
|
||||
const createCustomer = async (firstName: string, lastName: string, email: string) => {
|
||||
try {
|
||||
if (!jwt) {
|
||||
return;
|
||||
}
|
||||
// create customer
|
||||
const response = await fetch(`${process.env.NEXT_PUBLIC_BASE_URL}/store/customers`, {
|
||||
credentials: 'include',
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: `Bearer ${jwt}`,
|
||||
'x-publishable-api-key': process.env.NEXT_PUBLIC_MEDUSA_PUBLISHABLE_KEY as string,
|
||||
},
|
||||
body: JSON.stringify({
|
||||
first_name: firstName,
|
||||
last_name: lastName,
|
||||
email,
|
||||
}),
|
||||
}).then((res) => res.json());
|
||||
|
||||
setCustomer(response.customer);
|
||||
} catch (error) {
|
||||
console.error('Login error:', error);
|
||||
// Optionally, handle error (e.g., show error message to user)
|
||||
throw error; // Re-throw to allow caller to handle it
|
||||
}
|
||||
};
|
||||
return (
|
||||
<CustomerContext.Provider
|
||||
value={{
|
||||
customer,
|
||||
register,
|
||||
login,
|
||||
logout,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</CustomerContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
export const useCustomer = () => {
|
||||
const context = useContext(CustomerContext);
|
||||
|
||||
if (!context) {
|
||||
throw new Error('useCustomer must be used within a CustomerProvider');
|
||||
}
|
||||
|
||||
return context;
|
||||
};
|
||||
76
src/providers/region.tsx
Normal file
76
src/providers/region.tsx
Normal file
@ -0,0 +1,76 @@
|
||||
'use client';
|
||||
|
||||
import { HttpTypes } from '@medusajs/types';
|
||||
import React, { createContext, useContext, useEffect, useState } from 'react';
|
||||
|
||||
type RegionContextType = {
|
||||
region?: HttpTypes.StoreRegion;
|
||||
setRegion: React.Dispatch<React.SetStateAction<HttpTypes.StoreRegion | undefined>>;
|
||||
};
|
||||
|
||||
const RegionContext = createContext<RegionContextType | null>(null);
|
||||
|
||||
type RegionProviderProps = {
|
||||
children: React.ReactNode;
|
||||
};
|
||||
|
||||
export const RegionProvider = ({ children }: RegionProviderProps) => {
|
||||
const [region, setRegion] = useState<HttpTypes.StoreRegion>();
|
||||
|
||||
useEffect(() => {
|
||||
if (region) {
|
||||
// set its ID in the local storage in
|
||||
// case it changed
|
||||
localStorage.setItem('region_id', region.id);
|
||||
return;
|
||||
}
|
||||
|
||||
const regionId = localStorage.getItem('region_id');
|
||||
if (!regionId) {
|
||||
// retrieve regions and select the first one
|
||||
fetch(`${process.env.NEXT_PUBLIC_BASE_URL}/store/regions`, {
|
||||
credentials: 'include',
|
||||
headers: {
|
||||
'x-publishable-api-key': process.env.NEXT_PUBLIC_MEDUSA_PUBLISHABLE_KEY as string,
|
||||
},
|
||||
})
|
||||
.then((res) => res.json())
|
||||
.then(({ regions }) => {
|
||||
setRegion(regions[0]);
|
||||
});
|
||||
} else {
|
||||
// retrieve selected region
|
||||
fetch(`${process.env.NEXT_PUBLIC_BASE_URL}/store/regions/${regionId}`, {
|
||||
credentials: 'include',
|
||||
headers: {
|
||||
'x-publishable-api-key': process.env.NEXT_PUBLIC_MEDUSA_PUBLISHABLE_KEY as string,
|
||||
},
|
||||
})
|
||||
.then((res) => res.json())
|
||||
.then(({ region: dataRegion }) => {
|
||||
setRegion(dataRegion);
|
||||
});
|
||||
}
|
||||
}, [region]);
|
||||
|
||||
return (
|
||||
<RegionContext.Provider
|
||||
value={{
|
||||
region,
|
||||
setRegion,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</RegionContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
export const useRegion = () => {
|
||||
const context = useContext(RegionContext);
|
||||
|
||||
if (!context) {
|
||||
throw new Error('useRegion must be used within a RegionProvider');
|
||||
}
|
||||
|
||||
return context;
|
||||
};
|
||||
Reference in New Issue
Block a user