-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #3 from nguyentrongbut/task/reviews-tab
feat(reviews-tabs): display total rating and display reviews
- Loading branch information
Showing
16 changed files
with
476 additions
and
8 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
27 changes: 27 additions & 0 deletions
27
src/app/title/[slug]/reviews/@component/rating.read.only.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
import IconStar from "@/components/icon/icon.star"; | ||
import IconStarBorder from "@/components/icon/icon.star.border"; | ||
import IconStarHalf from "@/components/icon/icon.star.half"; | ||
|
||
const RatingReadOnly = (props: { stars: number, half?: boolean }) => { | ||
const { stars, half = false } = props; | ||
|
||
const rattingStar = Array.from({ length: 5 }, (elm, index) => { | ||
let number = index + 0.5; | ||
|
||
return ( | ||
<span key={index}> | ||
{stars >= index + 1 || (stars > index && stars < index + 1 && stars % 1 > 0.7) ? ( | ||
<IconStar className="primary-color size-5" /> | ||
) : stars >= number ? ( | ||
<IconStarHalf className={half ? "primary-color" : ""} /> | ||
) : ( | ||
<IconStarBorder className={`size-5 ${half ? "primary-color" : ""}`} /> | ||
)} | ||
</span> | ||
); | ||
}); | ||
|
||
return <div className="flex">{rattingStar}</div>; | ||
}; | ||
|
||
export default RatingReadOnly; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
"use client"; | ||
import IconStar from "@/components/icon/icon.star"; | ||
import IconStarBorder from "@/components/icon/icon.star.border"; | ||
import { useState } from "react"; | ||
|
||
const Rating = () => { | ||
const [rating, setRating] = useState<number | null>(null); | ||
const [hover, setHover] = useState<number | null>(null); | ||
|
||
return ( | ||
<div className="flex h-8 my-auto"> | ||
{[...Array(5)].map((star, index) => { | ||
const currentRating = index + 1; | ||
return ( | ||
<label | ||
key={index} | ||
className="relative flex items-center cursor-pointer transition-all py-1 pl-1" | ||
onMouseEnter={() => setHover(currentRating)} | ||
onMouseLeave={() => setHover(null)} | ||
> | ||
<input | ||
type="radio" | ||
name="rating" | ||
value={currentRating} | ||
onClick={() => setRating(currentRating)} | ||
className="hidden" | ||
/> | ||
<IconStarBorder className="cursor-pointer" /> | ||
<IconStar | ||
className={`absolute top-0 left-0 z-10 transition-all primary-color w-full h-full py-1 pl-1 ${ | ||
currentRating <= (hover ?? rating ?? 0) | ||
? "visible" | ||
: "invisible" | ||
}`} | ||
/> | ||
</label> | ||
); | ||
})} | ||
</div> | ||
); | ||
}; | ||
|
||
export default Rating; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,106 @@ | ||
const ReviewsTab = () => { | ||
return <div>this is reviews tab</div>; | ||
import TabContent from "@/app/title/@component/tab.content.wrapper"; | ||
import Rating from "@/app/title/[slug]/reviews/@component/rating"; | ||
import IconSend from "@/components/icon/icon.send"; | ||
import IconStarBorder from "@/components/icon/icon.star.border"; | ||
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"; | ||
import { Button } from "@/components/ui/button"; | ||
import { Separator } from "@/components/ui/separator"; | ||
import { Textarea } from "@/components/ui/textarea"; | ||
import { sendRequest } from "@/utils/api"; | ||
import Link from "next/link"; | ||
|
||
import dayjs from "dayjs"; | ||
import relativeTime from "dayjs/plugin/relativeTime"; | ||
import RatingReadOnly from "@/app/title/[slug]/reviews/@component/rating.read.only"; | ||
|
||
dayjs.extend(relativeTime); | ||
|
||
const ReviewsTab = async (props: any) => { | ||
const { params } = props; | ||
const temp = params?.slug?.split(".html" ?? []); | ||
const temp1 = temp[0]?.split("-" ?? []) as string[]; | ||
const id = temp1[temp1.length - 1]; | ||
|
||
const data = await sendRequest<IReviews[]>({ | ||
url: `${process.env.WEB_COMIC_API}/api/reviews?comicId=${id}&_expand=author`, | ||
method: "GET", | ||
nextOption: { | ||
cache: "no-store", | ||
}, | ||
}); | ||
|
||
return ( | ||
<TabContent> | ||
<Rating></Rating> | ||
<div className="relative"> | ||
<Textarea className="min-h-[7rem] mt-4 bg-neutral-100 border border-[#a3a3a3] focus:border-[var(--primary-color)] focus:outline-none transition-all peer"></Textarea> | ||
<label className="absolute top-2 left-4 text-neutral-700 transition-all rounded text-base pointer-events-none peer-focus-within:text-xs peer-focus-within:-top-2 peer-focus-within:bg-white px-1 -mx-1 max-w-[calc(100% - 2rem)]"> | ||
<span className="relative z-[2] overflow-hidden whitespace-nowrap"> | ||
Write a review | ||
</span> | ||
<div className="absolute left-0 bottom-0 w-full h-1/2 z-[1] bg-neutral-100"></div> | ||
</label> | ||
</div> | ||
<div className="flex justify-end mt-6"> | ||
<Button className="flex gap-1 items-center bg-primary-color p-1.5"> | ||
<IconSend></IconSend> | ||
<span>Submit</span> | ||
</Button> | ||
</div> | ||
<Separator className="mt-4 bg-neutral-300"></Separator> | ||
<section> | ||
{data.length >= 1 ? ( | ||
<ul> | ||
{data?.map((reviews) => { | ||
return ( | ||
<li | ||
className="flex gap-4 mt-4" | ||
key={reviews?.id} | ||
> | ||
<Link href="/author"> | ||
<Avatar className="size-12 mb-auto"> | ||
<AvatarImage | ||
src={reviews?.author?.avatar} | ||
alt={reviews?.author?.name} | ||
/> | ||
<AvatarFallback> | ||
{reviews?.author?.name} | ||
</AvatarFallback> | ||
</Avatar> | ||
</Link> | ||
<div className="flex flex-col gap-1"> | ||
<Link href="/author"> | ||
<h3 className="font-medium line-clamp-1 break-all"> | ||
{reviews?.author?.name} | ||
<span className="text-sm opacity-70 ml-1"> | ||
@{reviews?.author?.name} | ||
</span> | ||
</h3> | ||
</Link> | ||
<div className="flex -ml-0.5"> | ||
<RatingReadOnly | ||
stars={reviews?.rated} | ||
></RatingReadOnly> | ||
<div className="text-neutral-500 whitespace-nowrap text-sm mt-1 ml-2"> | ||
{dayjs( | ||
reviews?.createdAt | ||
).fromNow()} | ||
</div> | ||
</div> | ||
<p className="mt-2"> | ||
{reviews?.content} | ||
</p> | ||
</div> | ||
</li> | ||
); | ||
})} | ||
</ul> | ||
) : ( | ||
<div className="mt-4">No Reviews</div> | ||
)} | ||
</section> | ||
</TabContent> | ||
); | ||
}; | ||
|
||
export default ReviewsTab; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
import { ComponentProps } from "react"; | ||
|
||
const IconSend = (props: ComponentProps<"svg">) => { | ||
return ( | ||
<svg | ||
data-v-b8aef6c3="" | ||
xmlns="http://www.w3.org/2000/svg" | ||
width="20" | ||
height="20" | ||
viewBox="0 0 24 24" | ||
fill="none" | ||
stroke="currentColor" | ||
strokeWidth={2} | ||
strokeLinecap="round" | ||
strokeLinejoin="round" | ||
{...props} | ||
> | ||
<path d="M10 14l11 -11"></path> | ||
<path d="M21 3l-6.5 18a.55 .55 0 0 1 -1 0l-3.5 -7l-7 -3.5a.55 .55 0 0 1 0 -1l18 -6.5"></path> | ||
</svg> | ||
); | ||
}; | ||
|
||
export default IconSend; |
Oops, something went wrong.