Image tags are fucked for right now.

Cart is working
This commit is contained in:
2024-10-08 10:05:58 +05:30
parent 990e7ee75b
commit 4df566df5e
47 changed files with 1113 additions and 1204 deletions

164
src/providers/cart.tsx Normal file
View 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
View 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
View 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;
};