Skip to content

Commit

Permalink
Add redux for cart states
Browse files Browse the repository at this point in the history
  • Loading branch information
ckng0221 committed Feb 2, 2024
1 parent 4e64ffc commit 746ff02
Show file tree
Hide file tree
Showing 9 changed files with 234 additions and 78 deletions.
2 changes: 2 additions & 0 deletions apps/ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
"@mui/icons-material": "^5.15.2",
"@mui/material": "^5.15.2",
"@mui/x-data-grid": "^6.18.6",
"@reduxjs/toolkit": "^2.1.0",
"axios": "^1.6.2",
"bootstrap": "^5.3.2",
"dayjs": "^1.11.10",
Expand All @@ -23,6 +24,7 @@
"react-bootstrap": "^2.9.1",
"react-cookie": "^7.0.2",
"react-dom": "^18.2.0",
"react-redux": "^9.1.0",
"socket.io-client": "^4.7.2",
"universal-cookie": "^7.0.2"
},
Expand Down
21 changes: 13 additions & 8 deletions apps/ui/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,15 @@ import Home from './pages/Home';
import Layout from './pages/Layout';
import Login from './pages/Login';
import SignUp from './pages/Signup';
// Redux
import { useAppDispatch, useAppSelector } from './app/hooks';
import { setCartItems } from './app/cart/cartItem';

function App() {
const [cartItems, setCartItems] = useState<ICart[]>([]);
// Redux
const cartItems = useAppSelector((state) => state.cartitems.value);
const dispatch = useAppDispatch();
// const [cartItems, setCartItems] = useState<ICart[]>([]);
const [customer, setCustomer] = useState<ICustomer>({
_id: '',
name: '',
Expand Down Expand Up @@ -74,7 +80,7 @@ function App() {

const results = await Promise.all([customerPromise, cartsPromise]);
setCustomer(results[0].data);
setCartItems(results[1].data);
dispatch(setCartItems(results[1].data));
}
}
initialLoad();
Expand All @@ -90,7 +96,6 @@ function App() {
children?: React.ReactNode;
}) => {
const prevLocation = useLocation();
// console.log('🚀 ~ App ~ prevLocation:', prevLocation);

// FIXME: Not a good approach, as usertoken is not verified.
// Need to click twice
Expand Down Expand Up @@ -130,8 +135,8 @@ function App() {
element={
<BookDetails
customer={customer}
cartItems={cartItems}
setCartItems={setCartItems}
// cartItems={cartItems}
// setCartItems={setCartItems}
/>
}
/>
Expand All @@ -157,15 +162,15 @@ function App() {
path="checkout"
element={
<Checkout
cartItems={cartItems}
setCartItems={setCartItems}
// cartItems={cartItems}
// setCartItems={setCartItems}
customer={customer}
/>
}
/>

{/* Private */}
<Route element={<ProtectedRoute isAllowed={!!customer._id} />}>
{/* TODO: put checkout path in protected route, while don't have issue */}
<Route path="account" element={<Account customer={customer} />} />
<Route
path="borrowings"
Expand Down
21 changes: 21 additions & 0 deletions apps/ui/src/app/cart/cartitem.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { createSlice } from '@reduxjs/toolkit';

export const cartItemsSlice = createSlice({
name: 'cartitems',
initialState: {
value: [],
},
reducers: {
addToCart: (state, action) => {
state.value.push(action.payload);
},
setCartItems: (state, action) => {
state.value = action.payload;
},
},
});

// Action creators are generated for each case reducer function
export const { addToCart, setCartItems } = cartItemsSlice.actions;

export default cartItemsSlice.reducer;
7 changes: 7 additions & 0 deletions apps/ui/src/app/hooks.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { useDispatch, useSelector, useStore } from 'react-redux';
import type { AppDispatch, AppStore, RootState } from './store';

// Use throughout your app instead of plain `useDispatch` and `useSelector`
export const useAppDispatch = useDispatch.withTypes<AppDispatch>();
export const useAppSelector = useSelector.withTypes<RootState>();
export const useAppStore = useStore.withTypes<AppStore>();
14 changes: 14 additions & 0 deletions apps/ui/src/app/store.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { configureStore } from '@reduxjs/toolkit';
import cartItemsReducer from './cart/cartItem';

export const store = configureStore({
reducer: {
cartitems: cartItemsReducer,
},
});

// Infer the `RootState` and `AppDispatch` types from the store itself
export type RootState = ReturnType<typeof store.getState>;
// Inferred type: {posts: PostsState, comments: CommentsState, users: UsersState}
export type AppDispatch = typeof store.dispatch;
export type AppStore = typeof store;
16 changes: 10 additions & 6 deletions apps/ui/src/main.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App.tsx'
import './index.css'
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App.tsx';
import './index.css';
import { Provider } from 'react-redux';
import { store } from './app/store.ts';

ReactDOM.createRoot(document.getElementById('root')!).render(
<React.StrictMode>
<App />
<Provider store={store}>
<App />
</Provider>
</React.StrictMode>,
)
);
14 changes: 10 additions & 4 deletions apps/ui/src/pages/BookDetails.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,20 @@ import AlertComp from '../components/Alert';
import { Link as RouterLink } from 'react-router-dom';
import { ICustomer } from '../interfaces/customer';
import { createCart, updateCartById } from '../api/customer-api';
import { useAppDispatch, useAppSelector } from '../app/hooks';
import { setCartItems } from '../app/cart/cartItem';

interface IProps {
customer: ICustomer;
cartItems: ICart[];
setCartItems: (array: ICart[]) => void;
// cartItems: ICart[];
// setCartItems: (array: ICart[]) => void;
}

function BookDetails(props: IProps) {
// Redux
const cartItems = useAppSelector((state) => state.cartitems.value);
const dispatch = useAppDispatch();

const { bookId } = useParams();
const [snackOpen, setSnackOpen] = useState(false);
const navigate = useNavigate();
Expand All @@ -49,7 +55,7 @@ function BookDetails(props: IProps) {
// console.log(prevLocation);
navigate(`/login?redirectTo=${prevLocation.pathname}`);
}
const cart = props.cartItems.find((item) => item.book_id === bookId);
const cart = cartItems.find((item) => item.book_id === bookId);
console.log(cart);

if (cart?._id) {
Expand All @@ -66,7 +72,7 @@ function BookDetails(props: IProps) {
const cartItemRes = await createCart(cartItem);
// console.log(cartItemRes);

props.setCartItems([...props.cartItems, cartItemRes.data]);
dispatch(setCartItems([...cartItems, cartItemRes.data]));
}

setSnackOpen(true);
Expand Down
126 changes: 66 additions & 60 deletions apps/ui/src/pages/Checkout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,58 +27,22 @@ import { IBorrowing } from '../interfaces/borrowing';
import { ICart } from '../interfaces/cart';
import { ICustomer } from '../interfaces/customer';
import { IPayment } from '../interfaces/payment';

const ListItems = ({
cartItems,
setCartItems,
}: {
cartItems: IProps['cartItems'];
setCartItems: IProps['setCartItems'];
}) => {
function handleRemoveCartItem(cartId: string) {
const remainingCartItems = cartItems.filter((cart) => cart._id !== cartId);
setCartItems(remainingCartItems);

deleteCartById(cartId);

// console.log(cartId);
}

return cartItems.map((item) => {
return (
<ListItem key={item._id}>
<ListItemAvatar>
<Avatar>
<MenuBookIcon />
</Avatar>
</ListItemAvatar>
<ListItemText
primary={item.book_title}
secondary={`Quantity : ${item.quantity}`}
/>
<IconButton
aria-label="Remove cart item"
size="small"
onClick={() => {
handleRemoveCartItem(item._id);
}}
>
<DeleteIcon fontSize="inherit" color="error" />
</IconButton>
</ListItem>
);
});
};
import { useAppDispatch, useAppSelector } from '../app/hooks';
import { setCartItems } from '../app/cart/cartItem';

interface IProps {
cartItems: ICart[];
setCartItems: React.Dispatch<React.SetStateAction<ICart[]>>;
// cartItems: ICart[];
// setCartItems: React.Dispatch<React.SetStateAction<ICart[]>>;
customer: ICustomer;
}

// function processCheckout

function Checkout(props: IProps) {
// Redux
const cartItems = useAppSelector((state) => state.cartitems.value);
const dispatch = useAppDispatch();

const [show, setShow] = useState(false);
const [showLoading, setShowLoading] = useState(false);
const [body, setBody] = useState('Confirm to checkout your cart?');
Expand Down Expand Up @@ -106,7 +70,7 @@ function Checkout(props: IProps) {
const handleConfirm = async () => {
setShowLoading(true);

const borrowingBooks = props.cartItems.map((item) => {
const borrowingBooks = cartItems.map((item) => {
return {
id: item.book_id,
name: item.book_title,
Expand Down Expand Up @@ -158,20 +122,21 @@ function Checkout(props: IProps) {
// console.log('payment_initial', payment);
// console.log('payments', payments);

await sleep(1000);
// console.log('lala', props.cartItems);
sleep(1000).then(() => {
// console.log('lala', props.cartItems);

props.cartItems.map((cart) => {
// console.log('cartid', cart._id);
cartItems.map((cart) => {
// console.log('cartid', cart._id);

if (!cart._id) return;
deleteCartById(cart?._id);
if (!cart._id) return;
deleteCartById(cart?._id);
});
dispatch(setCartItems([]));
//delete cart in db
setShowLoading(false);
setShow(false);
setPaymentDialogOpen(true);
});
props.setCartItems([]);
//delete cart in db
setShowLoading(false);
setShow(false);
setPaymentDialogOpen(true);
} catch (error) {
console.error(error);
setShowLoading(false);
Expand Down Expand Up @@ -212,14 +177,14 @@ function Checkout(props: IProps) {
</Typography>
</CardContent>
<CardContent>
{props.cartItems.length > 0 ? (
{cartItems.length > 0 ? (
<List
sx={{ width: '100%', maxWidth: 360, bgcolor: 'background.paper' }}
>
<ListItem>Customer Name: {props.customer.name}</ListItem>
<ListItems
cartItems={props.cartItems}
setCartItems={props.setCartItems}
// cartItems={props.cartItems}
// setCartItems={props.setCartItems}
/>
</List>
) : (
Expand All @@ -229,7 +194,7 @@ function Checkout(props: IProps) {
</Card>

<p></p>
{props.cartItems.length > 0 && (
{cartItems.length > 0 && (
<Button variant="contained" onClick={handleShow}>
Checkout
</Button>
Expand Down Expand Up @@ -270,4 +235,45 @@ function Checkout(props: IProps) {
);
}

const ListItems = () => {
// Redux
const cartItems = useAppSelector((state) => state.cartitems.value);
const dispatch = useAppDispatch();

function handleRemoveCartItem(cartId: string) {
const remainingCartItems = cartItems.filter((cart) => cart._id !== cartId);
console.log(remainingCartItems);
dispatch(setCartItems(remainingCartItems));

deleteCartById(cartId);

// console.log(cartId);
}

return cartItems.map((item) => {
return (
<ListItem key={item._id}>
<ListItemAvatar>
<Avatar>
<MenuBookIcon />
</Avatar>
</ListItemAvatar>
<ListItemText
primary={item.book_title}
secondary={`Quantity : ${item.quantity}`}
/>
<IconButton
aria-label="Remove cart item"
size="small"
onClick={() => {
handleRemoveCartItem(item._id);
}}
>
<DeleteIcon fontSize="inherit" color="error" />
</IconButton>
</ListItem>
);
});
};

export default Checkout;
Loading

0 comments on commit 746ff02

Please sign in to comment.