Skip to content

Commit

Permalink
Merge pull request #164 from kitcc-org/67-breadcrumbs
Browse files Browse the repository at this point in the history
パンくずリストを作成した
  • Loading branch information
kimurash authored Dec 16, 2024
2 parents d316f4c + 483bdbe commit f219839
Show file tree
Hide file tree
Showing 13 changed files with 209 additions and 35 deletions.
5 changes: 5 additions & 0 deletions frontend/app/components/books/BookListComponent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@ import type { UseFormReturnType } from '@mantine/form';
import { SerializeFrom } from '@remix-run/cloudflare';
import { getBooksResponse } from 'client/client';
import type { GetBooksParams } from 'client/client.schemas';
import { LuBookCopy } from 'react-icons/lu';
import { PaginationProps } from '~/types/pagination';
import BookSearchComponent from '../book-search/BookSearchComponent';
import BreadCrumbsComponent from '../common/breadcrumbs/BreadCrumbsComponent';
import ErrorComponent from '../common/error/ErrorComponent';
import ContentsHeader from '../common/pagination/ContentsHeader';
import PaginationComponent from '../common/pagination/PaginationComponent';
Expand Down Expand Up @@ -34,6 +36,9 @@ const BookListComponent = ({
}: BookListComponentProps) => {
return (
<Stack bg="var(--mantine-color-body)" align="stretch" justify="flex-start">
<BreadCrumbsComponent
anchors={[{ icon: <LuBookCopy />, title: '蔵書一覧', href: '/home' }]}
/>
<BookSearchComponent
disclosure={{ isOpen, open, close }}
form={form}
Expand Down
7 changes: 7 additions & 0 deletions frontend/app/components/cart/CartListComponent.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { Stack } from '@mantine/core';
import { useAtom } from 'jotai';
import { LuShoppingCart } from 'react-icons/lu';
import { cartAtom } from '~/stores/cartAtom';
import BreadCrumbsComponent from '../common/breadcrumbs/BreadCrumbsComponent';
import CartCards from './CartCards';
import CartSelectedDialog from './CartSelectedDialog';
import CartTitle from './CartTitle';
Expand All @@ -10,6 +12,11 @@ const CartListComponent = () => {
const [cart] = useAtom(cartAtom);
return (
<Stack bg="var(--mantine-color-body)" align="stretch" justify="flex-start">
<BreadCrumbsComponent
anchors={[
{ icon: <LuShoppingCart />, title: '貸出カート', href: '/home/cart' },
]}
/>
<CartTitle />
{cart.length == 0 ? (
<NoCartComponent />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { Anchor, Breadcrumbs, Group, Text } from '@mantine/core';
import React from 'react';

interface AnchorProps {
icon: React.ReactNode;
title: string;
href: string;
}

interface BreadCrumbsComponentProps {
anchors: AnchorProps[];
}

const BreadCrumbsComponent = ({ anchors }: BreadCrumbsComponentProps) => {
return (
<Breadcrumbs my="xs">
{anchors.map((item, index) => (
<Anchor href={item.href} key={index}>
<Group gap="xs">
{item.icon}
<Text span truncate="end">
{item.title}
</Text>
</Group>
</Anchor>
))}
</Breadcrumbs>
);
};

export default BreadCrumbsComponent;
11 changes: 11 additions & 0 deletions frontend/app/components/global-books/GlobalBookListComponent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ import { SerializeFrom } from '@remix-run/cloudflare';
import type { searchGoogleBooksResponse } from 'client/client';
import type { SearchGoogleBooksParams } from 'client/client.schemas';
import React from 'react';
import { AiOutlineGlobal } from 'react-icons/ai';
import { PaginationProps } from '~/types/pagination';
import BreadCrumbsComponent from '../common/breadcrumbs/BreadCrumbsComponent';
import ErrorComponent from '../common/error/ErrorComponent';
import ContentsHeader from '../common/pagination/ContentsHeader';
import PaginationComponent from '../common/pagination/PaginationComponent';
Expand Down Expand Up @@ -45,6 +47,15 @@ const GlobalBookListComponent = ({
}: GlobalBookListComponentProps) => {
return (
<Stack bg="var(--mantine-color-body)" align="stretch" justify="flex-start">
<BreadCrumbsComponent
anchors={[
{
icon: <AiOutlineGlobal />,
title: 'グローバル検索',
href: '/home/global',
},
]}
/>
<GlobalBookSearchComponent
disclosure={{ isOpen, open, close }}
form={form}
Expand Down
12 changes: 12 additions & 0 deletions frontend/app/components/me-edit/MyPageEditComponent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import MyPageEditProfileFieldSet from './MyPageEditProfileFieldSet';
import MyPageEditPasswordFieldSet from './MyPageEditPasswordFieldSet';
import FormLayout from '../layouts/FormLayout';
import MyPageEditSubmitButton from './MyPageEditSubmitButton';
import BreadCrumbsComponent from '../common/breadcrumbs/BreadCrumbsComponent';
import { FaUser, FaUserEdit } from 'react-icons/fa';

interface MyPageEditComponentProps {
form: UseFormReturnType<
Expand All @@ -21,6 +23,16 @@ const MyPageEditComponent = ({
}: MyPageEditComponentProps) => {
return (
<Container size="sm">
<BreadCrumbsComponent
anchors={[
{ icon: <FaUser />, title: 'マイページ', href: '/home/me' },
{
icon: <FaUserEdit />,
title: 'プロフィール更新',
href: '/home/me/edit',
},
]}
/>
<FormLayout<UpdateUserFormBody> form={form} handleSubmit={handleSubmit}>
<MyPageEditTitle />
<Space h="xs" />
Expand Down
9 changes: 7 additions & 2 deletions frontend/app/components/me/MyPageComponent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@ import { Stack } from '@mantine/core';
import { SerializeFrom } from '@remix-run/cloudflare';
import { getLoansResponse } from 'client/client';
import { User } from 'client/client.schemas';
import { FaUser } from 'react-icons/fa';
import { PaginationProps } from '~/types/pagination';
import BreadCrumbsComponent from '../common/breadcrumbs/BreadCrumbsComponent';
import MyLoansListComponent from './MyLoansListComponent';
import MyProfileDataComponent from './MyProfileComponent';
import MyProfileComponent from './MyProfileComponent';

interface MyPageComponentProps {
user: User;
Expand All @@ -19,7 +21,10 @@ const MyPageComponent = ({
}: MyPageComponentProps) => {
return (
<Stack align="stretch" justify="center">
<MyProfileDataComponent name={user.name} email={user.email} />
<BreadCrumbsComponent
anchors={[{ icon: <FaUser />, title: 'マイページ', href: '/home/me' }]}
/>
<MyProfileComponent name={user.name} email={user.email} />
<MyLoansListComponent
loansResponse={loansResponse}
paginationProps={paginationProps}
Expand Down
2 changes: 1 addition & 1 deletion frontend/app/components/me/MyProfileComponent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ const MyProfileComponent = ({ name, email }: MyProfileComponentProps) => {
return (
<Center>
<Paper shadow="xs" p="xl" withBorder w="50%" maw={rem(500)}>
<Stack gap={rem(0)}>
<Stack gap={rem(10)}>
<Group justify="center" gap="0px">
<Avatar variant="white" radius="sm" size={rem(100)} />
<Text size={rem(30)}>{name}</Text>
Expand Down
13 changes: 13 additions & 0 deletions frontend/app/components/user-create/UserCreateComponent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ import { Container } from '@mantine/core';
import UserCreatePasswordForm from './UserCreatePasswordForm';
import UserCreateSubmitButton from './UserCreateSubmitButton';
import UserCreatePasswordComponent from './UserCreatePasswordComponent';
import BreadCrumbsComponent from '../common/breadcrumbs/BreadCrumbsComponent';
import { FaUsers } from 'react-icons/fa';
import { FaUserPlus } from 'react-icons/fa6';

interface UserCreateComponentProps {
form: UseFormReturnType<
Expand All @@ -30,6 +33,16 @@ const UserCreateComponent = ({
return (
<Container size="sm">
<FormLayout<CreateUserBody> form={form} handleSubmit={handleSubmit}>
<BreadCrumbsComponent
anchors={[
{ icon: <FaUsers />, title: 'ユーザー一覧', href: '/home/users' },
{
icon: <FaUserPlus />,
title: 'ユーザー作成',
href: '/home/users/create',
},
]}
/>
<UserCreateTitle />
<UserCreateNameForm form={form} />
<UserCreateEmailForm form={form} />
Expand Down
47 changes: 28 additions & 19 deletions frontend/app/components/users/UsersListComponent.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { Stack } from '@mantine/core';
import { SerializeFrom } from '@remix-run/cloudflare';
import { getUsersResponse } from 'client/client';
import { FaUsers } from 'react-icons/fa';
import type { PaginationProps } from '~/types/pagination';
import BreadCrumbsComponent from '../common/breadcrumbs/BreadCrumbsComponent';
import ErrorComponent from '../common/error/ErrorComponent';
import ContentsHeader from '../common/pagination/ContentsHeader';
import PaginationComponent from '../common/pagination/PaginationComponent';
Expand All @@ -19,29 +21,36 @@ const UsersListComponent = ({
usersResponse,
}: UsersListComponentProps) => {
return (
<Stack bg="var(--mantine-color-body)" align="center" justify="flex-start">
<UsersListTitle />
<Stack w={{ base: '70%', md: '50%' }} align="stretch">
<ContentsHeader
<>
<BreadCrumbsComponent
anchors={[
{ icon: <FaUsers />, title: 'ユーザー一覧', href: '/home/users' },
]}
/>
<Stack bg="var(--mantine-color-body)" align="center" justify="flex-start">
<UsersListTitle />
<Stack w={{ base: '70%', md: '50%' }} align="stretch">
<ContentsHeader
page={paginationProps.page}
limit={paginationProps.limit}
total={paginationProps.total}
handleLimitChange={paginationProps.handleLimitChange}
/>
{usersResponse.status === 200 ? (
<UsersListTable users={usersResponse.data.users} />
) : (
<ErrorComponent message={'ユーザー情報を取得できませんでした。'} />
)}
</Stack>
<UserCreateButton />
<PaginationComponent
total={paginationProps.total}
page={paginationProps.page}
limit={paginationProps.limit}
total={paginationProps.total}
handleLimitChange={paginationProps.handleLimitChange}
handlePaginationChange={paginationProps.handlePaginationChange}
/>
{usersResponse.status === 200 ? (
<UsersListTable users={usersResponse.data.users} />
) : (
<ErrorComponent message={'ユーザー情報を取得できませんでした。'} />
)}
</Stack>
<UserCreateButton />
<PaginationComponent
total={paginationProps.total}
page={paginationProps.page}
limit={paginationProps.limit}
handlePaginationChange={paginationProps.handlePaginationChange}
/>
</Stack>
</>
);
};

Expand Down
36 changes: 33 additions & 3 deletions frontend/app/routes/home.books.$bookId/route.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,17 @@ import type {
LoaderFunctionArgs,
} from '@remix-run/cloudflare';
import { json, redirect } from '@remix-run/cloudflare';
import { Outlet, useLoaderData } from '@remix-run/react';
import { Outlet, useLoaderData, useLocation } from '@remix-run/react';
import type { getBookResponse, getLoansResponse } from 'client/client';
import { deleteBook, getBook, getLoans } from 'client/client';
import { commitSession, getSession } from '~/services/session.server';

import { Book } from 'client/client.schemas';
import { FaBook } from 'react-icons/fa6';
import { LuBookCopy } from 'react-icons/lu';
import { TbBookUpload } from 'react-icons/tb';
import BookDetailActionPanel from '~/components/book-detail/BookDetailActionPanel';
import BreadCrumbsComponent from '~/components/common/breadcrumbs/BreadCrumbsComponent';
import ErrorComponent from '~/components/common/error/ErrorComponent';
import { commitSession, getSession } from '~/services/session.server';
import { ActionResponse } from '~/types/response';
import { makeCookieHeader } from '~/utils/session';

Expand Down Expand Up @@ -117,6 +120,7 @@ export const action = async ({ request }: ActionFunctionArgs) => {

const BookDetail = () => {
const { bookResponse, loansResponse } = useLoaderData<typeof loader>();
const location = useLocation();
switch (bookResponse.status) {
case 400:
return <ErrorComponent message="リクエストが不正です" />;
Expand All @@ -127,6 +131,32 @@ const BookDetail = () => {
}
return (
<Stack bg="var(--mantine-color-body)" align="stretch" justify="flex-start">
<BreadCrumbsComponent
anchors={
location.pathname.includes('/edit')
? [
{ icon: <LuBookCopy />, title: '蔵書一覧', href: '/home' },
{
icon: <FaBook />,
title: bookResponse.data.title,
href: `/home/books/${bookResponse.data.id}`,
},
{
icon: <TbBookUpload />,
title: '書籍更新',
href: `/home/books/${bookResponse.data.id}/edit`,
},
]
: [
{ icon: <LuBookCopy />, title: '蔵書一覧', href: '/home' },
{
icon: <FaBook />,
title: bookResponse.data.title,
href: `/home/books/${bookResponse.data.id}`,
},
]
}
/>
<Grid gutter={rem(50)}>
<Grid.Col span={3}>
<BookDetailActionPanel book={bookResponse.data} />
Expand Down
43 changes: 33 additions & 10 deletions frontend/app/routes/home.global.$volumeId/route.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@ import {
import { useLoaderData } from '@remix-run/react';
import { createBook, getBooks, getGoogleBook } from 'client/client';
import { CreateBookBody, GoogleBook } from 'client/client.schemas';
import { AiOutlineGlobal } from 'react-icons/ai';
import { FaBookAtlas } from 'react-icons/fa6';
import BookDetailActionPanel from '~/components/book-detail/BookDetailActionPanel';
import BreadCrumbsComponent from '~/components/common/breadcrumbs/BreadCrumbsComponent';
import GlobalBookDetailContent from '~/components/global-book-detail/GlobalBookDetailContent';
import { commitSession, getSession } from '~/services/session.server';
import { ActionResponse } from '~/types/response';
Expand Down Expand Up @@ -101,16 +104,36 @@ export const action = async ({ request }: ActionFunctionArgs) => {
const GlobalBookDetailPage = () => {
const { googleBook, totalBook, bookId } = useLoaderData<LoaderData>();
return (
<Stack bg="var(--mantine-color-body)" align="stretch" justify="flex-start">
<Grid gutter={rem(50)}>
<Grid.Col span={3}>
<BookDetailActionPanel book={googleBook} totalBook={totalBook} />
</Grid.Col>
<Grid.Col span={9}>
<GlobalBookDetailContent book={googleBook} bookId={bookId} />
</Grid.Col>
</Grid>
</Stack>
<>
<BreadCrumbsComponent
anchors={[
{
icon: <AiOutlineGlobal />,
title: 'グローバル検索',
href: '/home/global',
},
{
icon: <FaBookAtlas />,
title: googleBook.title,
href: `/home/global/${googleBook.id}`,
},
]}
/>
<Stack
bg="var(--mantine-color-body)"
align="stretch"
justify="flex-start"
>
<Grid gutter={rem(50)}>
<Grid.Col span={3}>
<BookDetailActionPanel book={googleBook} totalBook={totalBook} />
</Grid.Col>
<Grid.Col span={9}>
<GlobalBookDetailContent book={googleBook} bookId={bookId} />
</Grid.Col>
</Grid>
</Stack>
</>
);
};

Expand Down
14 changes: 14 additions & 0 deletions frontend/test/routes/home._index/route.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -182,4 +182,18 @@ describe('Book List Page', () => {
}
});
});

describe('Breadcrumbs', () => {
it('should navigate to home page when clicked', async () => {
const { user } = customRender(
<BookListPageStub initialEntries={['/home']} />,
);

const homeLink = await screen.findByRole('link', { name: '蔵書一覧' });
await user.click(homeLink);

// ホームページに遷移する
expect(window.location.pathname).toBe('/home');
});
});
});
Loading

0 comments on commit f219839

Please sign in to comment.