implemented ratings, fixes

This commit is contained in:
lnn0q 2025-06-01 17:54:57 +03:00
parent 4801cf3a13
commit d1da980501
31 changed files with 775 additions and 236 deletions

View file

@ -4,7 +4,8 @@ import { BookChapterDeleteMutation } from '@generated/types';
import { Link } from '@tanstack/react-router';
import { bookFragment } from '../fragments';
import { useToastMutation } from '../hooks/useToastMutation';
import { bookChapterListQuery } from '../pages/work/book/$bookId/overview';
import { bookChapterQuery } from '../pages/work/book/$bookId/chapter/$chapterId/view';
import { bookChapterListQuery } from '../pages/work/book/$bookId/overview/contents';
export function BookChapterCard({
bookId,
@ -20,7 +21,7 @@ export function BookChapterCard({
const { update: chapterDelete } = useToastMutation<BookChapterDeleteMutation>({
mutation,
successMsg: 'Chapter deleted successfully',
refetchQueries: [bookChapterListQuery]
refetchQueries: [bookChapterListQuery, bookChapterQuery]
});
return (
<Card.Root width="100%" variant="elevated">

View file

@ -82,7 +82,7 @@ export function BookChapterForm({
</Box>
<Stack direction="row">
<Button onClick={async () => onSubmit()}>Submit</Button>
<Link to={`/work/book/${bookId}/overview`}>
<Link to={`/work/book/${bookId}/overview/contents`}>
<Button>Go back</Button>
</Link>
</Stack>

View file

@ -0,0 +1,23 @@
import { Tabs } from '@chakra-ui/react';
import { Link } from '@tanstack/react-router';
export function BookOverviewTabs({
bookId,
currentTab
}: {
readonly bookId: string;
readonly currentTab: 'contents' | 'reviews';
}) {
return (
<Tabs.Root defaultValue="members" value={currentTab}>
<Tabs.List>
<Link to={`/work/book/${bookId}/overview/contents`}>
<Tabs.Trigger value="contents">Contents</Tabs.Trigger>
</Link>
<Link to={`/work/book/${bookId}/overview/reviews`}>
<Tabs.Trigger value="reviews">Reviews</Tabs.Trigger>
</Link>
</Tabs.List>
</Tabs.Root>
);
}

View file

@ -0,0 +1,48 @@
import { Button } from '@app/client/components/ui/button';
import { Icon, Stack } from '@chakra-ui/react';
import { RatingType } from '@generated/types';
export function RatingPanel({
personalRating,
onRatingSet
}: {
readonly personalRating: RatingType;
readonly onRatingSet: (rating: RatingType) => Promise<void>;
}) {
return (
<Stack direction="row">
<Button
size="sm"
backgroundColor={personalRating === RatingType.Up ? 'green.500/80' : 'green.500'}
borderColor="green.500"
onClick={async () => {
if (personalRating !== RatingType.Up) {
await onRatingSet(RatingType.Up);
return;
}
await onRatingSet(RatingType.None);
}}
>
<Icon>
<i className="nf nf-oct-thumbsup" />
</Icon>
</Button>
<Button
size="sm"
backgroundColor={personalRating === RatingType.Down ? 'red.500/80' : 'red.500'}
borderColor="red.500"
onClick={async () => {
if (personalRating !== RatingType.Down) {
await onRatingSet(RatingType.Down);
return;
}
await onRatingSet(RatingType.None);
}}
>
<Icon>
<i className="nf nf-oct-thumbsdown" />
</Icon>
</Button>
</Stack>
);
}

View file

@ -0,0 +1,54 @@
import { Avatar } from '@app/client/components/ui/avatar';
import { toFormattedDate } from '@app/client/helpers';
import { Box, Card, Stack, Text } from '@chakra-ui/react';
import { Link } from '@tanstack/react-router';
export function ReviewCard({
review
}: {
readonly review: {
readonly id: string;
readonly username: string;
readonly createdAt: string;
readonly text: string;
};
}) {
return (
<Stack direction="row" alignItems="start">
<Link to={`/profile/${review.username}`}>
<Avatar size="sm" fallback={review.username[0]} />
</Link>
<Card.Root variant="subtle" width="100%">
<Card.Body paddingX="3" paddingY="1">
<Stack direction="row" justifyContent="space-between" alignItems="center" gap={1}>
<Link to={`/profile/${review.username}`}>
<Text
color="colorPalette.400"
_hover={{
textDecoration: 'underline',
textDecorationColor: 'colorPalette.700'
}}
>
{review.username}
</Text>
</Link>
<Text color="fg.muted">{toFormattedDate({ value: review.createdAt })}</Text>
</Stack>
<Box width="100%" position="relative" padding={0}>
<Text>{review.text}</Text>
{/* <Button */}
{/* size="sm" */}
{/* position="absolute" */}
{/* right="2px" */}
{/* bottom="5px" */}
{/* padding={0} */}
{/* variant="ghost" */}
{/* // onClick={async () => onReviewSubmit(text)} */}
{/* ></Button> */}
</Box>
</Card.Body>
</Card.Root>
</Stack>
);
}

View file

@ -0,0 +1,47 @@
import { Avatar } from '@app/client/components/ui/avatar';
import { Button, Icon, Stack, Textarea } from '@chakra-ui/react';
import { useState } from 'react';
export function ReviewInput({
username,
onReviewSubmit
}: {
readonly username: string;
readonly onReviewSubmit: (text: string) => Promise<void>;
}) {
const [text, setText] = useState<string>('');
return (
<Stack direction="row" alignItems="start">
<Avatar size="sm" fallback={username[0]} />
<Stack width="100%" position="relative" borderRadius="md">
<Textarea
variant="subtle"
placeholder="Write your review here..."
resize="none"
maxLength={700}
maxLines={10}
fontSize="md"
autoresize
value={text}
onChange={event => setText(event.target.value)}
/>
<Button
size="sm"
position="absolute"
right="2px"
bottom="5px"
padding={0}
variant="ghost"
onClick={async () => {
await onReviewSubmit(text);
setText('');
}}
>
<Icon>
<i className="nf nf-fa-send" />
</Icon>
</Button>
</Stack>
</Stack>
);
}

View file

@ -22,9 +22,9 @@ export function WorkingListCard({
<Card.Root width="100%" variant="elevated">
<Card.Body>
<Stack>
<Stack direction="row" justifyContent="space-between">
<Stack direction={'row'} justifyContent="space-between">
<Text as="div" minWidth={0}>
<Link to={`/work/book/${id}/overview`}>
<Link to={`/work/book/${id}/overview/contents`}>
<Heading
color="colorPalette.400"
_hover={{
@ -37,7 +37,7 @@ export function WorkingListCard({
</Link>
</Text>
<Stack direction="row" justifyContent="center" alignItems="center">
<Stack direction="row" justifyContent="center" alignItems="center" hideBelow="md">
<WorkingRatingBadge ratingTotal={ratingTotal} />
<WorkingReviewsBadge reviewsTotal={reviewsTotal} />
<WorkingProgressStatusBadge progressStatus={progressStatus} />
@ -60,7 +60,7 @@ export function WorkingListCard({
</Link>
</Stack>
<Stack direction="row">
<Stack maxWidth="100%" direction="row" overflow="auto" flexWrap="wrap">
<Box>
<Badge size="md" variant="solid">
<Stack direction="row">
@ -88,6 +88,12 @@ export function WorkingListCard({
</Stack>
</Stack>
<Stack direction="row" justifyContent="left" alignItems="center" hideFrom="md">
<WorkingRatingBadge ratingTotal={ratingTotal} />
<WorkingReviewsBadge reviewsTotal={reviewsTotal} />
<WorkingProgressStatusBadge progressStatus={progressStatus} />
</Stack>
<Stack>
<Text lineClamp={4}>{description}</Text>
</Stack>