Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

本の追加ページ(グローバル詳細ページ)の実装 #127

Merged
merged 9 commits into from
Nov 13, 2024
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
20 changes: 20 additions & 0 deletions frontend/app/components/book-detail/BookDetailControlButtons.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import BookDetailEditButton from './BookDetailEditButton';
import { MdDeleteForever } from 'react-icons/md';
import { Button } from '@mantine/core';
import { useFetcher } from '@remix-run/react';
import BookDetailDeleteButton from './BookDetailDeleteButton';

interface BookDetailControlButtonsProps {
id: number;
}

const BookDetailControlButtons = ({ id }: BookDetailControlButtonsProps) => {
return (
<>
<BookDetailEditButton bookId={id} />
<BookDetailDeleteButton bookId={id} />
</>
);
};

export default BookDetailControlButtons;
43 changes: 20 additions & 23 deletions frontend/app/components/book-detail/BookDetailControlPanel.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,27 @@
import { Button, Stack } from '@mantine/core';
import { useFetcher } from '@remix-run/react';
import { Stack } from '@mantine/core';
import { useAtom } from 'jotai';
import { MdDeleteForever } from 'react-icons/md';
import { userAtom } from '~/stores/userAtom';
import BookDetailEditButton from './BookDetailEditButton';
import BookDetailThumbnail from './BookDetailThumbnail';
import BookDetailControlButtons from './BookDetailControlButtons';
import { useLocation } from '@remix-run/react';
import GlobalBookDetailControlButtons from '../global-book-detail/GlobalBookDetailControlButtons';
import { SearchBooks200BooksItem } from 'client/client.schemas';

interface BookDetailControlPanelProps {
id: number;
id?: number;
thumbnail?: string;
searchBook?: SearchBooks200BooksItem;
totalBook?: number;
}

const BookDetailControlPanel = ({
id,
thumbnail,
searchBook,
totalBook,
}: BookDetailControlPanelProps) => {
const [user] = useAtom(userAtom);
const fetcher = useFetcher();
const location = useLocation();

return (
<Stack
Expand All @@ -26,23 +31,15 @@ const BookDetailControlPanel = ({
gap="md"
>
<BookDetailThumbnail thumbnail={thumbnail} />
{!!user && <BookDetailEditButton bookId={id} />}
{!!user && (
<Button
color="red"
leftSection={<MdDeleteForever />}
fz="lg"
onClick={() =>
fetcher.submit(
{ bookId: id },
{ action: '/home/books/$bookId', method: 'DELETE' },
)
}
disabled={fetcher.state === 'submitting'}
>
削除
</Button>
)}
{user && location.pathname.includes('global')
? searchBook &&
typeof totalBook == 'number' && (
<GlobalBookDetailControlButtons
searchBook={searchBook}
totalBook={totalBook}
/>
)
: id && <BookDetailControlButtons id={id} />}
</Stack>
);
};
Expand Down
30 changes: 30 additions & 0 deletions frontend/app/components/book-detail/BookDetailDeleteButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import React from 'react';
import { MdDeleteForever } from 'react-icons/md';
import { Button } from '@mantine/core';
import { useFetcher } from '@remix-run/react';

interface BookDetailDeleteButtonProps {
bookId: number;
}

const BookDetailDeleteButton = ({ bookId }: BookDetailDeleteButtonProps) => {
const fetcher = useFetcher();
return (
<Button
color="red"
leftSection={<MdDeleteForever />}
fz="lg"
onClick={() =>
fetcher.submit(
{ bookId: bookId },
{ action: '/home/books/$bookId', method: 'DELETE' },
)
}
disabled={fetcher.state === 'submitting'}
>
削除
</Button>
);
};

export default BookDetailDeleteButton;
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { Badge } from '@mantine/core';

interface GlobalBookDetailAuthorBadgeProps {
name: string;
}

const GlobalBookDetailAuthorBadge = ({
name,
}: GlobalBookDetailAuthorBadgeProps) => {
return (
<Badge
component="a"
color="teal"
href={`/home/global?author=${name}`}
style={{ cursor: 'pointer' }}
variant="outline"
size="lg"
>
{name}
</Badge>
);
};

export default GlobalBookDetailAuthorBadge;
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { Stack } from '@mantine/core';
import { SearchBooks200BooksItem } from 'client/client.schemas';
import GlobalBookDetailContentTable from './GlobalBookDetailContentTable';
import BookDetailTitle from '../book-detail/BookDetailTitle';
import BookDetailDescription from '../book-detail/BookDetailDescription';
import GlobalBookDetailLink from './GlobalBookDetailLink';

interface GlobalBookDetailContentProps {
book: SearchBooks200BooksItem;
bookId?: number;
}

const GlobalBookDetailContent = ({
book,
bookId,
}: GlobalBookDetailContentProps) => {
return (
<Stack
bg="var(--mantine-color-body)"
align="stretch"
justify="flex-start"
gap="xl"
>
<BookDetailTitle title={book.title} />
<GlobalBookDetailContentTable book={book} />
<BookDetailDescription description={book.description ?? ''} />
{!!bookId && <GlobalBookDetailLink bookId={bookId} />}
</Stack>
);
};

export default GlobalBookDetailContent;
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { Group, rem, Stack, Table, Text } from '@mantine/core';
import { SearchBooks200BooksItem } from 'client/client.schemas';
import GlobalBookDetailAuthorBadge from './GlobalBookDetailAuthorBadge';

interface GlobalBookDetailContentTableProps {
book: SearchBooks200BooksItem;
}

const GlobalBookDetailContentTable = ({
book,
}: GlobalBookDetailContentTableProps) => {
return (
<Stack gap="sm" align="stretch" justify="flex-start">
<Text fz={rem(22)}>書籍情報</Text>
<Table fz={rem(17)}>
<Table.Tr key={'author'}>
<Table.Th>著者</Table.Th>
<Table.Td>
<Group gap={rem(7)}>
{book.authors.map((author, id) => (
<GlobalBookDetailAuthorBadge key={id} name={author} />
))}
</Group>
</Table.Td>
</Table.Tr>
<Table.Tr key={'publisher'}>
<Table.Th>出版社</Table.Th>
<Table.Td>{book.publisher}</Table.Td>
</Table.Tr>
<Table.Tr key={'publishedDate'}>
<Table.Th>出版日</Table.Th>
<Table.Td>{book.publishedDate}</Table.Td>
</Table.Tr>
<Table.Tr key={'isbn'}>
<Table.Th>ISBN</Table.Th>
<Table.Td>{book.isbn}</Table.Td>
</Table.Tr>
</Table>
</Stack>
);
};

export default GlobalBookDetailContentTable;
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { Button } from '@mantine/core';
import { useFetcher } from '@remix-run/react';
import { CreateBookBody, SearchBooks200BooksItem } from 'client/client.schemas';
import { BiSolidBookAdd } from 'react-icons/bi';

interface GlobalBookDetailControlButtonsProps {
searchBook: SearchBooks200BooksItem;
totalBook: number;
}

const GlobalBookDetailControlButtons = ({
searchBook,
totalBook,
}: GlobalBookDetailControlButtonsProps) => {
const fetcher = useFetcher<CreateBookBody>();
const addBookData: CreateBookBody = {
authors: searchBook.authors,
description: searchBook.description ?? '',
isbn: searchBook.isbn ?? '',
publishedDate: searchBook.publishedDate ?? '',
publisher: searchBook.publisher ?? '',
stock: 1,
thumbnail: searchBook.thumbnail,
title: searchBook.title,
};
return (
<Button
variant="filled"
color="teal"
disabled={totalBook > 0}
leftSection={<BiSolidBookAdd />}
onClick={() =>
fetcher.submit(addBookData, {
action: '/home/global/books/$bookId',
method: 'POST',
})
}
>
{totalBook > 0 ? '追加済み' : '追加'}
</Button>
);
};

export default GlobalBookDetailControlButtons;
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { Anchor, Text } from '@mantine/core';

interface GlobalBookDetailLinkProps {
bookId: number;
}

const GlobalBookDetailLink = ({ bookId }: GlobalBookDetailLinkProps) => {
return (
<Text>
この書籍はすでに登録されています。蔵書の詳細ページは
<Anchor href={`/home/books/${bookId}`}>こちら</Anchor>
</Text>
);
};

export default GlobalBookDetailLink;
3 changes: 2 additions & 1 deletion frontend/app/components/global-books/GlobalBookCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ const GlobalBookCard = ({ book }: GlobalBookCardProps) => {
<Card shadow="sm" radius="md" pb="xs" withBorder>
<Card.Section withBorder inheritPadding py="xs">
<BookCardThumbnail
id={book.isbn ? Number(book.isbn) : undefined}
// Google Books APIでisbnの味の検索ができるまではコメントアウト
// FIXME id={book.isbn ? Number(book.isbn) : undefined}
thumbnail={book.thumbnail}
/>
</Card.Section>
Expand Down
2 changes: 1 addition & 1 deletion frontend/app/routes/home._index/route.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ interface LoaderData {
};
}

interface ActionResponse {
export interface ActionResponse {
method: string;
status: number;
}
Expand Down
Loading
Loading