Skip to content

Commit

Permalink
Merge pull request #133 from kitcc-org/72-cart-page
Browse files Browse the repository at this point in the history
カートページの実装
  • Loading branch information
Kosei805 authored Nov 14, 2024
2 parents 8c9bf9c + 569de83 commit 41c6fb1
Show file tree
Hide file tree
Showing 20 changed files with 507 additions and 22 deletions.
14 changes: 12 additions & 2 deletions frontend/app/components/books/BookCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,23 @@ const BookCard = ({ book }: BookCardProps) => {
return (
<Card shadow="sm" radius="md" pb="xs" withBorder>
<Card.Section withBorder inheritPadding>
<BookCardHeader id={book.id} stock={book.stock} />
<BookCardHeader
id={book.id}
stock={book.stock}
thumbnail={book.thumbnail}
/>
</Card.Section>
<Card.Section withBorder inheritPadding py="xs">
<BookCardThumbnail id={book.id} thumbnail={book.thumbnail} />
</Card.Section>

{!!user && <BookCardFooter id={book.id} stock={book.stock} />}
{!!user && (
<BookCardFooter
id={book.id}
stock={book.stock}
thumbnail={book.thumbnail}
/>
)}
</Card>
);
};
Expand Down
10 changes: 8 additions & 2 deletions frontend/app/components/books/BookCardCartButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,22 @@ import { Button } from '@mantine/core';
import { useAtom } from 'jotai';
import { BiSolidCartAdd } from 'react-icons/bi';
import { cartAtom } from '~/stores/cartAtom';
import { addBooksToCart } from '~/utils/cart';

interface BookCardCartButtonProps {
id: number;
stock: number;
thumbnail?: string;
}

const BookCardCartButton = ({ id, stock }: BookCardCartButtonProps) => {
const BookCardCartButton = ({
id,
stock,
thumbnail,
}: BookCardCartButtonProps) => {
const [cart, setCart] = useAtom(cartAtom);
const addCart = () => {
setCart([...cart, { id, stock }]);
setCart(addBooksToCart(cart, [{ id, stock, thumbnail }]));
};
return (
<Button
Expand Down
5 changes: 3 additions & 2 deletions frontend/app/components/books/BookCardFooter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,13 @@ import BookCardCartButton from './BookCardCartButton';
interface BookCardFooterProps {
id: number;
stock: number;
thumbnail?: string;
}

const BookCardFooter = ({ id, stock }: BookCardFooterProps) => {
const BookCardFooter = ({ id, stock, thumbnail }: BookCardFooterProps) => {
return (
<Center pt={10}>
<BookCardCartButton id={id} stock={stock} />
<BookCardCartButton id={id} stock={stock} thumbnail={thumbnail} />
</Center>
);
};
Expand Down
18 changes: 9 additions & 9 deletions frontend/app/components/books/BookCardHeader.tsx
Original file line number Diff line number Diff line change
@@ -1,29 +1,29 @@
import { Checkbox, Group } from '@mantine/core';
import { useAtom } from 'jotai';
import { selectedBooksAtom } from '~/stores/bookAtom';
import type { CartProps } from '~/stores/cartAtom';
import { SelectedBookProps, selectedBooksAtom } from '~/stores/bookAtom';
import { userAtom } from '~/stores/userAtom';
import BookCardHeaderBadge from './BookCardHeaderBadge';

interface BookCardHeaderProps {
id: number;
stock: number;
thumbnail?: string;
}

const BookCardHeader = ({ id, stock }: BookCardHeaderProps) => {
const BookCardHeader = ({ id, stock, thumbnail }: BookCardHeaderProps) => {
const [selectedBook, setSelectedBook] = useAtom(selectedBooksAtom);
const [user] = useAtom(userAtom);
// 選択されている本のIDと表示する本のIDを比較する関数
const selectedCheck = (element: CartProps) => element.id === id;
const isSelected = (element: SelectedBookProps) => element.id === id;

const selectedBookAdd = () => {
const switchBookSelect = () => {
// チェックボックスの状態が変化した時に
if (selectedBook.some(selectedCheck)) {
if (selectedBook.some(isSelected)) {
// すでに選択されていた場合は選択を外す
setSelectedBook(selectedBook.filter((element) => element.id !== id));
} else {
// 選択されていなかった場合は選択する
setSelectedBook([...selectedBook, { id, stock }]);
setSelectedBook([...selectedBook, { id, stock, thumbnail }]);
}
};

Expand All @@ -32,8 +32,8 @@ const BookCardHeader = ({ id, stock }: BookCardHeaderProps) => {
{!!user && (
<Checkbox
value={id}
checked={selectedBook.some(selectedCheck)}
onChange={selectedBookAdd}
checked={selectedBook.some(isSelected)}
onChange={switchBookSelect}
/>
)}
<BookCardHeaderBadge stock={stock} />
Expand Down
13 changes: 10 additions & 3 deletions frontend/app/components/books/BookCardThumbnail.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { AspectRatio, Image, Overlay, rem } from '@mantine/core';
import { useNavigate } from '@remix-run/react';
import { useLocation, useNavigate } from '@remix-run/react';
import NoImage from '~/img/noImage.png';

interface BookCardThumbnailProps {
Expand All @@ -9,12 +9,19 @@ interface BookCardThumbnailProps {

const BookCardThumbnail = ({ id, thumbnail }: BookCardThumbnailProps) => {
const navigate = useNavigate();
const location = useLocation();
const toNavigate = location.pathname.includes('global')
? location.pathname + `/books/${id}`
: `/home/books/${id}`;
return (
<AspectRatio
ratio={10 / 14}
style={{ flex: `0 0 ${rem(400)}`, cursor: id ? 'pointer' : undefined }}
style={{
flex: `0 0 ${rem(400)}`,
cursor: id ? 'pointer' : undefined,
}}
component="div"
onClick={() => id && navigate(`books/${id}`)}
onClick={() => id && navigate(toNavigate)}
>
<Image src={thumbnail ? thumbnail : NoImage} alt="Book cover" />
{!id && <Overlay color="gray" backgroundOpacity={0.5} />}
Expand Down
1 change: 1 addition & 0 deletions frontend/app/components/books/BookCards.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ const BookCards = ({ books }: BookCardsProps) => {
'1400px': 6,
'1700px': 7,
}}
// 画面幅が300pxを超えた場合、カードの間をxlにする
spacing={{ base: 10, '300px': 'xl' }}
>
{books.map((book) => (
Expand Down
3 changes: 2 additions & 1 deletion frontend/app/components/books/BookSelectedDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { useSubmit } from '@remix-run/react';
import { useAtom } from 'jotai';
import { selectedBooksAtom } from '~/stores/bookAtom';
import { cartAtom } from '~/stores/cartAtom';
import { addBooksToCart } from '~/utils/cart';
import { successNotification } from '~/utils/notification';

const BookSelectedDialog = () => {
Expand All @@ -29,7 +30,7 @@ const BookSelectedDialog = () => {
fz="xs"
color="yellow"
onClick={() => {
setCart([...cart, ...selectedBook]);
setCart(addBooksToCart(cart, selectedBook));
setSelectedBook([]);

successNotification('カートに追加しました');
Expand Down
28 changes: 28 additions & 0 deletions frontend/app/components/cart/CartCard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { Card } from '@mantine/core';
import { CartProps } from '~/stores/cartAtom';
import BookCardThumbnail from '../books/BookCardThumbnail';
import CartCardHeader from './CartCardHeader';

interface CartCardProps {
book: CartProps;
}

const CartCard = ({ book }: CartCardProps) => {
return (
<Card shadow="sm" radius="md" pb="xs" withBorder>
<Card.Section withBorder inheritPadding>
<CartCardHeader
id={book.id}
stock={book.stock}
volume={book.volume}
thumbnail={book.thumbnail}
/>
</Card.Section>
<Card.Section withBorder inheritPadding py="xs">
<BookCardThumbnail id={book.id} thumbnail={book.thumbnail} />
</Card.Section>
</Card>
);
};

export default CartCard;
77 changes: 77 additions & 0 deletions frontend/app/components/cart/CartCardHeader.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import { Checkbox, Group } from '@mantine/core';
import { useAtom } from 'jotai';
import { cartAtom, CartProps, selectedCartBooksAtom } from '~/stores/cartAtom';
import CartCardNumberInput from './CartCardNumberInput';

interface CartCardHeaderProps {
id: number;
stock: number;
volume: number;
thumbnail?: string;
}

const CartCardHeader = ({
id,
stock,
volume,
thumbnail,
}: CartCardHeaderProps) => {
const [cart, setCart] = useAtom(cartAtom);
const [selectedCartBook, setSelectedCartBook] = useAtom(
selectedCartBooksAtom,
);

// 該当する本のvolumeを変更する
const handleChangeVolume = (id: number, value: number) => {
setCart(
cart.map((element) => {
if (element.id === id) {
return {
id: element.id,
stock: element.stock,
thumbnail: element.thumbnail,
volume: value,
};
}
return element;
}),
);
};

// 選択されている本のIDと表示する本のIDを比較する関数
const isSelected = (element: CartProps) => element.id === id;

const switchBookSelect = () => {
// チェックボックスの状態が変化した時に
if (selectedCartBook.some(isSelected)) {
// すでに選択されていた場合は選択を外す
setSelectedCartBook(
selectedCartBook.filter((element) => element.id !== id),
);
} else {
// 選択されていなかった場合は選択する
setSelectedCartBook([
...selectedCartBook,
{ id, stock, thumbnail, volume: 1 },
]);
}
};

return (
<Group justify="space-between" py={10}>
<Checkbox
value={id}
checked={selectedCartBook.some(isSelected)}
onChange={switchBookSelect}
/>
<CartCardNumberInput
id={id}
stock={stock}
volume={volume}
handleChangeVolume={handleChangeVolume}
/>
</Group>
);
};

export default CartCardHeader;
40 changes: 40 additions & 0 deletions frontend/app/components/cart/CartCardNumberInput.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { Group, Select, Text } from '@mantine/core';
import { range } from '@mantine/hooks';

interface CartCardHeaderBadgeProps {
id: number;
stock: number;
volume: number;
handleChangeVolume: (id: number, value: number) => void;
}

const CartCardNumberInput = ({
id,
stock,
volume,
handleChangeVolume,
}: CartCardHeaderBadgeProps) => {
const stockList = range(0, stock);
const dataList = stock >= volume ? stockList : [...stockList, volume];
const strList = dataList.map((data) => data.toString());

const handleOnChange = (volume: string | null) => {
if (!volume) return;
const numVolume = Number(volume);
handleChangeVolume(id, numVolume);
};
return (
<Group justify="flex-end" w="70%">
<Text>冊数</Text>
<Select
w="50%"
data={strList}
value={String(volume)}
error={volume > stock}
onChange={(value) => handleOnChange(value)}
/>
</Group>
);
};

export default CartCardNumberInput;
32 changes: 32 additions & 0 deletions frontend/app/components/cart/CartCards.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { ScrollArea, SimpleGrid } from '@mantine/core';
import { useAtom } from 'jotai';
import { cartAtom } from '~/stores/cartAtom';
import CartCard from './CartCard';

const CartCards = () => {
const [cart] = useAtom(cartAtom);

return (
<ScrollArea h="70dh">
<SimpleGrid
type="container"
cols={{
base: 2,
'500px': 3,
'800px': 4,
'1100px': 5,
'1400px': 6,
'1700px': 7,
}}
// 画面幅が300pxを超えた場合、カードの間をxlにする
spacing={{ base: 10, '300px': 'xl' }}
>
{cart.map((book, index) => (
<CartCard key={index} book={book} />
))}
</SimpleGrid>
</ScrollArea>
);
};

export default CartCards;
34 changes: 34 additions & 0 deletions frontend/app/components/cart/CartListComponent.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { Stack } from '@mantine/core';
import CartTitle from './CartTitle';
import CartCards from './CartCards';
import CartSelectedDialog from './CartSelectedDialog';
import { useAtom } from 'jotai';
import { cartAtom } from '~/stores/cartAtom';
import NoCartComponent from './NoCartComponent';

interface CartListComponentProps {
handleBorrowButtonClick: () => void;
}

const CartListComponent = ({
handleBorrowButtonClick,
}: CartListComponentProps) => {
const [cart] = useAtom(cartAtom);
return (
<Stack bg="var(--mantine-color-body)" align="stretch" justify="flex-start">
<CartTitle />
{cart.length == 0 ? (
<NoCartComponent />
) : (
<>
<CartCards />
<CartSelectedDialog
handleBorrowButtonClick={handleBorrowButtonClick}
/>
</>
)}
</Stack>
);
};

export default CartListComponent;
Loading

0 comments on commit 41c6fb1

Please sign in to comment.