185 lines
5.1 KiB
TypeScript
185 lines
5.1 KiB
TypeScript
import { gql, useQuery } from '@apollo/client';
|
|
import { PaginatedListSkeleton } from '@app/client/components/PaginatedListSkeleton';
|
|
import { Pagination } from '@app/client/components/Pagination';
|
|
import { WorkingProgressStatusBadge } from '@app/client/components/WorkingProgressStatusBadge';
|
|
import { WorkingRatingBadge } from '@app/client/components/WorkingRatingBadge';
|
|
import { WorkingReviewsBadge } from '@app/client/components/WorkingReviewsTotalBadge';
|
|
import { bookChapterFragment, bookFragment } from '@app/client/fragments';
|
|
import { toFormattedDate } from '@app/client/helpers';
|
|
import { usePaginatedQuery } from '@app/client/hooks/usePaginatedQuery';
|
|
import { BookOne } from '@app/client/interfaces';
|
|
import {
|
|
Heading,
|
|
HStack,
|
|
Skeleton,
|
|
SkeletonCircle,
|
|
SkeletonText,
|
|
Stack,
|
|
Text
|
|
} from '@chakra-ui/react';
|
|
import { BookChapterListQuery, BookOverviewQuery } from '@generated/types';
|
|
import { createFileRoute, Link } from '@tanstack/react-router';
|
|
import { useState } from 'react';
|
|
|
|
export const Route = createFileRoute('/work/book/$bookId')({
|
|
component: RouteComponent
|
|
});
|
|
|
|
function RouteComponent() {
|
|
const { bookId: id } = Route.useParams();
|
|
const { data: bookData } = useQuery<BookOverviewQuery>(bookQuery, { variables: { id } });
|
|
|
|
return (
|
|
<Stack width="100%" justifyContent="center" alignItems="center" padding={3}>
|
|
{bookData != null ? <BookOverview {...bookData.bookOne} /> : <BookOverviewSkeleton />}
|
|
</Stack>
|
|
);
|
|
}
|
|
|
|
function BookOverview({
|
|
id,
|
|
title,
|
|
description,
|
|
author,
|
|
progressStatus,
|
|
createdAt,
|
|
updatedAt,
|
|
wordsTotal,
|
|
chaptersTotal,
|
|
reviewsTotal,
|
|
ratingTotal
|
|
}: BookOne) {
|
|
return (
|
|
<Stack width="80%" maxWidth="840px" gap={3}>
|
|
<Stack direction="row" justifyContent="space-between" alignItems="center">
|
|
<Heading color="colorPalette.400" size="2xl" overflow="auto">
|
|
{title}
|
|
</Heading>
|
|
</Stack>
|
|
|
|
<Stack direction="column" gap={1}>
|
|
<Stack direction="row">
|
|
<Link>
|
|
<Text fontWeight="bold">by:</Text>
|
|
</Link>
|
|
<Link to={`/profile/${author.username}`}>
|
|
<Text
|
|
color="colorPalette.400"
|
|
_hover={{
|
|
textDecoration: 'underline',
|
|
textDecorationColor: 'colorPalette.700'
|
|
}}
|
|
>
|
|
{author.username}
|
|
</Text>
|
|
</Link>
|
|
</Stack>
|
|
|
|
<Stack direction="row">
|
|
<Text fontWeight="bold">created at:</Text>
|
|
<Text>{toFormattedDate({ value: createdAt })}</Text>
|
|
</Stack>
|
|
|
|
<Stack direction="row">
|
|
<Text fontWeight="bold">last updated:</Text>
|
|
<Text>{toFormattedDate({ value: updatedAt })}</Text>
|
|
</Stack>
|
|
|
|
<Stack direction="row">
|
|
<Text fontWeight="bold">chapters:</Text>
|
|
<Text>{chaptersTotal}</Text>
|
|
</Stack>
|
|
|
|
<Stack direction="row">
|
|
<Text fontWeight="bold">words:</Text>
|
|
<Text>{wordsTotal}</Text>
|
|
</Stack>
|
|
</Stack>
|
|
|
|
<Stack direction="row" justifyContent="left" alignItems="center">
|
|
<WorkingRatingBadge ratingTotal={ratingTotal} />
|
|
<WorkingReviewsBadge reviewsTotal={reviewsTotal} />
|
|
<WorkingProgressStatusBadge progressStatus={progressStatus} />
|
|
</Stack>
|
|
|
|
<Text>{description}</Text>
|
|
|
|
<BookChapterList id={id} />
|
|
</Stack>
|
|
);
|
|
}
|
|
|
|
function BookChapterList({ id }: { readonly id: string }) {
|
|
const [chaptersListPage, setChaptersListPage] = useState<number>(1);
|
|
const [chaptersListPageSize, setChaptersListPageSize] = useState<number>(10);
|
|
const startIndex = (chaptersListPage - 1) * chaptersListPageSize;
|
|
|
|
const { data } = usePaginatedQuery<BookChapterListQuery>({
|
|
query: bookChapterListQuery,
|
|
input: {
|
|
bookId: id,
|
|
filter: {
|
|
offset: startIndex,
|
|
size: chaptersListPageSize
|
|
}
|
|
}
|
|
});
|
|
|
|
return (
|
|
<Stack>
|
|
{data != null ? (
|
|
<>
|
|
<Stack>...chapters</Stack>
|
|
{data.bookChapterMany.total > chaptersListPageSize && (
|
|
<Stack justifyContent="center" alignItems="center">
|
|
<Pagination
|
|
total={data.bookChapterMany.total}
|
|
page={chaptersListPage}
|
|
setPage={setChaptersListPage}
|
|
pageSize={chaptersListPageSize}
|
|
setPageSize={setChaptersListPageSize}
|
|
/>
|
|
</Stack>
|
|
)}
|
|
</>
|
|
) : (
|
|
<PaginatedListSkeleton disableSearch />
|
|
)}
|
|
</Stack>
|
|
);
|
|
}
|
|
|
|
function BookOverviewSkeleton() {
|
|
return (
|
|
<Stack width="80%" maxWidth="840px">
|
|
<Stack gap="6" maxWidth="100%">
|
|
<HStack width="full">
|
|
<SkeletonCircle size="10" />
|
|
<SkeletonText noOfLines={5} />
|
|
</HStack>
|
|
<Skeleton height="200px" />
|
|
</Stack>
|
|
</Stack>
|
|
);
|
|
}
|
|
|
|
const bookQuery = gql`
|
|
query BookOverview($id: ID!) {
|
|
bookOne(id: $id) {
|
|
...Book
|
|
}
|
|
}
|
|
${bookFragment}
|
|
`;
|
|
|
|
const bookChapterListQuery = gql`
|
|
query BookChapterList($input: BookChapterManyInput!) {
|
|
bookChapterMany(input: $input) {
|
|
items {
|
|
...BookChapter
|
|
}
|
|
total
|
|
}
|
|
}
|
|
${bookChapterFragment}
|
|
`;
|