Skip to content

Commit

Permalink
refactor(nova): improved ratings tabs implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
mostafakamar2308 committed Dec 22, 2024
1 parent 6572d3c commit 6ac283a
Show file tree
Hide file tree
Showing 22 changed files with 475 additions and 304 deletions.
18 changes: 0 additions & 18 deletions apps/nova/src/components/TutorProfile/LoadingTutorInfo.tsx

This file was deleted.

100 changes: 59 additions & 41 deletions apps/nova/src/components/TutorProfile/Ratings.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { useFindTutorRatings } from "@litespace/headless/rating";
import React, { useCallback } from "react";
import { LoadingTutorInfo } from "@/components/TutorProfile/LoadingTutorInfo";
import { ratingOrganizer } from "@/lib/ratings";
import React, { useCallback, useMemo } from "react";
import { organizeRatings } from "@/lib/ratings";
import { useUser } from "@litespace/headless/context/user";
import {
TutorRatingCard,
Expand All @@ -13,22 +12,24 @@ import Star from "@litespace/assets/Star";
import NewTutor from "@litespace/assets/NewTutor";
import { Button, ButtonSize } from "@litespace/luna/Button";
import { isEmpty } from "lodash";
import { TutorLoadingError } from "@/components/TutorProfile/TutorLoadingError";
import { Loader, LoadingError } from "@litespace/luna/Loading";

const NoTutorRatings: React.FC<{ tutorName: string | null }> = ({
tutorName,
}) => {
const intl = useFormatMessage();
return (
<div className="flex flex-col mt-20 gap-14 items-center justify-center">
<NewTutor />
<div className="flex gap-[88px] items-center justify-center">
<Typography
element="h4"
element="subtitle-1"
weight="bold"
className="text-natural-950 text-center mb-20"
className="text-natural-950 text-center max-w-[476px]"
>
{intl("tutor.profile.first-rating", { tutor: tutorName })}
</Typography>
<div className="w-[292px] h-[294px]">
<NewTutor />
</div>
</div>
);
};
Expand All @@ -39,59 +40,76 @@ const Ratings: React.FC<{ id: number; tutorName: string | null }> = ({
}) => {
const { user } = useUser();
const intl = useFormatMessage();
const ratingsQuery = useFindTutorRatings(id);
const ratingsQuery = useFindTutorRatings(id, { page: 1, size: 30 });

const refetchRatings = useCallback(() => {
ratingsQuery.refetch();
}, [ratingsQuery]);

const ratings = useMemo(
() => organizeRatings(ratingsQuery.data?.list || [], user?.id),
[ratingsQuery.data, user]
);

if (ratingsQuery.isLoading || ratingsQuery.isPending)
return <LoadingTutorInfo />;
return (
<div className="h-96 flex justify-center items-center">
<Loader text={intl("tutor.profile.loading")} />
</div>
);

if (ratingsQuery.error || !user)
return <TutorLoadingError refetchRatings={refetchRatings} />;

const ratings = ratingOrganizer(ratingsQuery.data.list, user.id);
return (
<div className="h-96 flex items-center justify-center">
<LoadingError
error={intl("tutor.profile.error")}
retry={refetchRatings}
/>
</div>
);

return (
<div className="flex my-8 mx-10 gap-4 flex-wrap justify-center">
<div className="flex flex-col my-8 mx-10 gap-10 justify-center">
{isEmpty(ratings) ? <NoTutorRatings tutorName={tutorName} /> : null}

{ratings.map((rating) => {
if (
"userId" in rating &&
(rating.feedback || rating.userId === user.id)
)
return (
<TutorRatingCard
feedback={rating.feedback}
imageUrl={rating.image}
rating={rating.value}
studentId={rating.userId}
studentName={rating.name}
tutorName={tutorName}
owner={rating.userId === user.id}
/>
);
<div className="flex gap-4 flex-wrap justify-center">
{ratings.map((rating) => {
if (
"userId" in rating &&
(rating.feedback || rating.userId === user.id)
)
return (
<TutorRatingCard
feedback={rating.feedback}
imageUrl={rating.image}
rating={rating.value}
studentId={rating.userId}
studentName={rating.name}
tutorName={tutorName}
owner={rating.userId === user.id}
/>
);

if ("ratings" in rating)
return (
<TutorRatingCardGroup
ratings={rating.ratings}
tutorName={tutorName}
value={rating.value}
/>
);
})}
if ("ratings" in rating)
return (
<TutorRatingCardGroup
ratings={rating.ratings}
tutorName={tutorName}
value={rating.value}
/>
);
})}
</div>

<div className="flex gap-10 flex-col items-center justify-center mt-10">
<div className="flex gap-10 flex-col items-center justify-center ">
<Typography
element="subtitle-1"
weight="medium"
className="text-natural-950 w-2/3 text-center"
className="text-natural-950 text-center max-w-[912px]"
>
{intl("tutor.profile.your-ratings-help")}
</Typography>

<Button
size={ButtonSize.Small}
className="w-[386px] flex items-center gap-2"
Expand Down
24 changes: 0 additions & 24 deletions apps/nova/src/components/TutorProfile/TutorLoadingError.tsx

This file was deleted.

120 changes: 59 additions & 61 deletions apps/nova/src/components/TutorProfile/TutorTabs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -60,68 +60,66 @@ export const TutorTabs: React.FC<{
}, []);

return (
<div className="mt-12">
<Tabs.Root
value={tab}
onValueChange={(value: string) => setTab(value as Tab)}
>
<Tabs.List className="border-b border-natural-300 flex gap-[56px] ">
{tabs.map(({ value, label }) => (
<Tabs.Trigger
key={value}
value={value}
className={cn("py-2 relative")}
<Tabs.Root
value={tab}
onValueChange={(value: string) => setTab(value as Tab)}
>
<Tabs.List className="border-b border-natural-300 flex gap-[56px] ">
{tabs.map(({ value, label }) => (
<Tabs.Trigger
key={value}
value={value}
className={cn("py-2 relative")}
>
<Typography
element="body"
weight="semibold"
className={cn(
"transition-colors duration-300",
value === tab ? "text-brand-700" : "text-natural-500"
)}
>
<Typography
element="body"
weight="semibold"
className={cn(
"transition-colors duration-300",
value === tab ? "text-brand-700" : "text-natural-500"
)}
>
{intl(label)}
</Typography>
<AnimatePresence>
{tab === value ? (
<motion.div
initial={{ opacity: 0 }}
animate={{
opacity: 1,
transition: {
duration: 0.3,
},
}}
exit={{ opacity: 0 }}
className="absolute -bottom-[1px] left-0 w-full h-[3px] bg-brand-700 rounded-t-[10px]"
/>
) : null}
</AnimatePresence>
</Tabs.Trigger>
))}
</Tabs.List>

<div className="mt-8">
<AnimatePresence initial={false} mode="wait">
{tab === "profile" ? (
<Animate key="profile" tab="profile">
<ProfileInfo
about={tutor.about}
topics={tutor.topics}
video={tutor.video}
{intl(label)}
</Typography>
<AnimatePresence>
{tab === value ? (
<motion.div
initial={{ opacity: 0 }}
animate={{
opacity: 1,
transition: {
duration: 0.3,
},
}}
exit={{ opacity: 0 }}
className="absolute -bottom-[1px] left-0 w-full h-[3px] bg-brand-700 rounded-t-[10px]"
/>
</Animate>
) : null}
{tab === "ratings" ? (
<Animate key="ratings" tab="ratings">
<div className="min-h-96">
<Ratings tutorName={tutor.name} id={tutor.id} />
</div>
</Animate>
) : null}
</AnimatePresence>
</div>
</Tabs.Root>
</div>
) : null}
</AnimatePresence>
</Tabs.Trigger>
))}
</Tabs.List>

<div className="mt-8">
<AnimatePresence initial={false} mode="wait">
{tab === "profile" ? (
<Animate key="profile" tab="profile">
<ProfileInfo
about={tutor.about}
topics={tutor.topics}
video={tutor.video}
/>
</Animate>
) : null}
{tab === "ratings" ? (
<Animate key="ratings" tab="ratings">
<div className="min-h-96">
<Ratings tutorName={tutor.name} id={tutor.id} />
</div>
</Animate>
) : null}
</AnimatePresence>
</div>
</Tabs.Root>
);
};
17 changes: 5 additions & 12 deletions apps/nova/src/lib/ratings.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,14 @@
import { IRating } from "@litespace/types";
import { TutorRatingCardGroupProps } from "@litespace/luna/TutorFeedback";

export const ratingOrganizer = (
export const organizeRatings = (
ratings: IRating.FindTutorRatingsApiResponse["list"],
currentUserId: number
currentUserId: number | undefined
) => {
let currentUserRating: IRating.RateeRatings | undefined;
const ratingsWithFeedback: IRating.RateeRatings[] = [];
const ratingsWithoutFeedback: {
ratings: Array<{
name: string | null;

userId: number;

imageUrl: string | null;
}>;
value: number;
}[] = [];
const ratingsWithoutFeedback: Omit<TutorRatingCardGroupProps, "tutorName">[] =
[];

ratings.forEach((rating) => {
if (rating.userId === currentUserId) {
Expand Down
2 changes: 1 addition & 1 deletion apps/nova/src/pages/TutorProfile.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ const TutorProfile: React.FC = () => {
<span className="underline text-brand-700">{tutor.data.name}</span>
</Typography>
</div>
<div className="bg-natural-50 border border-natural-100 shadow-tutor-profile rounded-2xl p-10 mt-6">
<div className="bg-natural-50 border border-natural-100 shadow-tutor-profile rounded-2xl p-10 mt-6 flex flex-col gap-12">
<TutorProfileCard {...tutor.data} onBook={openDialog} />
<TutorTabs tutor={tutor.data} />
<BookLesson tutorId={tutor.data.id} close={closeDialog} open={open} />
Expand Down
4 changes: 2 additions & 2 deletions packages/assets/assets/exclaimation-mark-circle.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit 6ac283a

Please sign in to comment.