Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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());
Comment on lines +17 to +19
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

この3つは何のための変数だろう?

Copy link
Contributor Author

@Kosei805 Kosei805 Nov 13, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

もし、volumeをstockが超えてたら0 ~ stock, volumeの要素を持つ配列が選択肢になる。
そうでないなら0 ~ stockまでを選択肢とする

そういった選択肢の配列


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}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

便利だね

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

やったあ

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' }}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

画面幅が300ピクセル以上の場合に xl サイズの間隔が適用されると理解した.
一発で理解できなかったので,コメントを残しておきたい.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

コメント残しました

>
{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
Loading