feat: implement shopping cart

This commit is contained in:
jinhojang6 2024-03-05 21:14:32 +09:00
parent cb6cc845e5
commit 0e10083372
No known key found for this signature in database
GPG Key ID: 1762F21FE8B543F8
6 changed files with 221 additions and 32 deletions

View File

@ -19,6 +19,7 @@
"@testing-library/user-event": "14.4.3",
"@types/node": "18.15.5",
"@types/react": "18.0.30",
"axios": "^1.6.7",
"babel-plugin-styled-components": "2.0.7",
"electron-context-menu": "3.3.0",
"electron-is-dev": "2.0.0",

34
src/common/api.js Normal file
View File

@ -0,0 +1,34 @@
import axios, { AxiosError } from 'axios';
export const API_BASE = 'https://api.chec.io/v1';
const api = axios.create({
baseURL: API_BASE,
});
api.interceptors.request.use(
async (config) => {
return config;
},
(error) => {
Promise.reject(error);
},
);
api.interceptors.response.use(
(response) => {
return response;
},
async (error) => {
if (error.response?.status === 401) {
try {
console.log('error.response', error.response);
} catch (e) {
console.log('error', e);
}
}
return Promise.reject(error);
},
);
export { api };

110
src/common/shop/cart.js Normal file
View File

@ -0,0 +1,110 @@
const token = 'sk_test_56290c1603cc68a61b59eb003647fdb91940a2cdc5b31';
export async function createCart() {
try {
const url = new URL(`https://api.chec.io/v1/carts`);
const headers = {
'X-Authorization': token,
Accept: 'application/json',
'Content-Type': 'application/json',
};
const response = await fetch(url, {
method: 'GET',
headers: headers,
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
return data;
} catch (error) {
console.error('Error:', error);
throw error;
}
}
export async function getCart(cartId) {
try {
const url = new URL(`https://api.chec.io/v1/carts/${cartId}`);
const headers = {
'X-Authorization': token,
Accept: 'application/json',
'Content-Type': 'application/json',
};
const response = await fetch(url, {
method: 'GET',
headers: headers,
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
return data;
} catch (error) {
console.error('Error:', error);
throw error;
}
}
export async function addItemToCart(cartId, body) {
try {
const url = new URL(`https://api.chec.io/v1/carts/${cartId}`);
const headers = {
'X-Authorization': token,
Accept: 'application/json',
'Content-Type': 'application/json',
};
const response = await fetch(url, {
method: 'POST',
headers: headers,
body: JSON.stringify(body),
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
return data;
} catch (error) {
console.error('Error:', error);
throw error;
}
}
export async function removeItemFromCart(cartId, line_item_id) {
try {
const url = new URL(`https://api.chec.io/v1/carts/${cartId}/items/${line_item_id}`);
const headers = {
'X-Authorization': token,
Accept: 'application/json',
'Content-Type': 'application/json',
};
const response = await fetch(url, {
method: 'DELETE',
headers: headers,
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
return data;
} catch (error) {
console.error('Error:', error);
throw error;
}
}

View File

@ -6,6 +6,8 @@ import { TEMP_PRODUCTS_DATA } from './Shop';
import Categories from '../Shop/Categories';
import FooterSection from '../FooterSection';
import SidebarMenu from '../SidebarMenu';
import { useEffect, useState } from 'react';
import { createCart, addItemToCart } from '../../common/shop/cart';
const Image = styled.img`
max-width: unset !important;
@ -14,8 +16,31 @@ const Image = styled.img`
`;
const ProductItem = () => {
const { id } = useParams();
const { name, price, src, description } = TEMP_PRODUCTS_DATA.find((product) => String(product.id) === String(id));
const { id: productId } = useParams();
const { name, price, src, description } = TEMP_PRODUCTS_DATA.find((product) => String(product.id) === String(productId));
const [cartId, setCartId] = useState('');
const [quantity, setQuantity] = useState(1);
useEffect(() => {
const getCartId = async () => {
const cart = await createCart();
setCartId(cart?.id);
};
getCartId();
}, []);
const handlePurchase = async () => {
await addItemToCart(cartId, {
id: 'prod_8XO3wp77QNlYAz',
quantity: quantity,
});
alert('Item added to cart');
};
const handleQuantityChange = (e) => {
setQuantity(e.target.value);
};
return (
<>
@ -37,7 +62,7 @@ const ProductItem = () => {
<br />
<Categories />
<BoardsBox>
<div className='board' key={id}>
<div className='board' key={productId}>
<div className='boxbar'>
<h2>{name}</h2>
</div>
@ -49,12 +74,12 @@ const ProductItem = () => {
<br />
<div>
<p>Quantity:</p>
<input type='number' placeholder='Quantity' defaultValue={1} style={{ width: '50px' }} />
<input type='number' placeholder='Quantity' defaultValue={1} style={{ width: '50px' }} onChange={handleQuantityChange} />
</div>
<br />
<br />
<div>
<button>Buy</button>
<button onClick={handlePurchase}>Buy</button>
</div>
<br />
<br />

View File

@ -1,4 +1,4 @@
import React, { useEffect } from 'react';
import React, { useEffect, useRef } from 'react';
import { Header, Logo, AboutContent } from '../styled/views/Home.styled';
import { Link } from 'react-router-dom';
import { useParams } from 'react-router-dom';
@ -7,9 +7,14 @@ import { useNavigate } from 'react-router-dom';
const SignInAuth = () => {
const { id } = useParams();
const navigate = useNavigate();
const loaded = useRef(false);
useEffect(() => {
if (!id) return;
if (!loaded.current) {
loaded.current = true;
const getUser = async () => {
try {
const url = new URL('https://api.chec.io/v1/customers/exchange-token');
@ -28,8 +33,7 @@ const SignInAuth = () => {
const { customer_id, jwt } = response;
console.log('customer_id', customer_id);
console.log('jwt', jwt);
localStorage.setItem('login_token', id);
localStorage.setItem('customer_id', customer_id);
localStorage.setItem('jwt', jwt);
@ -39,7 +43,8 @@ const SignInAuth = () => {
}
};
getUser();
}, [id]);
}
}, [id, loaded]);
return (
<>

View File

@ -4616,6 +4616,15 @@ axios@^0.27.2:
follow-redirects "^1.14.9"
form-data "^4.0.0"
axios@^1.6.7:
version "1.6.7"
resolved "https://registry.yarnpkg.com/axios/-/axios-1.6.7.tgz#7b48c2e27c96f9c68a2f8f31e2ab19f59b06b0a7"
integrity sha512-/hDJGff6/c7u0hDkvkGxR/oy6CbCs8ziCsC7SqmhjfozqiJGc8Z11wrv9z9lYfY4K8l+H9TpjcMDX0xOZmx+RA==
dependencies:
follow-redirects "^1.15.4"
form-data "^4.0.0"
proxy-from-env "^1.1.0"
axobject-query@^3.1.1:
version "3.2.1"
resolved "https://registry.yarnpkg.com/axobject-query/-/axobject-query-3.2.1.tgz#39c378a6e3b06ca679f29138151e45b2b32da62a"
@ -8053,7 +8062,7 @@ follow-redirects@^1.0.0, follow-redirects@^1.14.9:
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.2.tgz#b460864144ba63f2681096f274c4e57026da2c13"
integrity sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==
follow-redirects@^1.14.0:
follow-redirects@^1.14.0, follow-redirects@^1.15.4:
version "1.15.5"
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.5.tgz#54d4d6d062c0fa7d9d17feb008461550e3ba8020"
integrity sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw==
@ -13213,6 +13222,11 @@ proxy-addr@~2.0.7:
forwarded "0.2.0"
ipaddr.js "1.9.1"
proxy-from-env@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2"
integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==
psl@^1.1.33:
version "1.9.0"
resolved "https://registry.yarnpkg.com/psl/-/psl-1.9.0.tgz#d0df2a137f00794565fcaf3b2c00cd09f8d5a5a7"