Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion apps/web/src/components/UpcomingLessons/Content.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ import { capture } from "@/lib/sentry";
import { Web } from "@litespace/utils/routes";
import { router } from "@/lib/routes";
import { isStudent } from "@litespace/utils/user";
import { range } from "lodash";
import { faker } from "@faker-js/faker/locale/ar";

type Lessons = ILesson.FindUserLessonsApiResponse["list"];

Expand Down Expand Up @@ -113,7 +115,7 @@ export const Content: React.FC<{

return (
<div>
<div className="grid grid-cols-[repeat(auto-fill,minmax(265px,1fr))] gap-x-2 md:gap-x-3 gap-y-4 md:gap-y-6">
<div className="grid grid-cols-[repeat(auto-fill,minmax(385px,1fr))] gap-x-2 md:gap-x-3 gap-y-4 md:gap-y-6">
{list.map((item) => {
const tutor = item.members.find(
(member) =>
Expand Down Expand Up @@ -172,6 +174,8 @@ export const Content: React.FC<{
otherMember.role === IUser.Role.Student
? "student"
: "tutor",
topics: range(7).map(() => faker.lorem.words(1)),
level: "C1",
Comment on lines +177 to +178
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is no English level for tutors. This should only appear in students cards for tutors.
Furthermore, lesson-card for students is different from lesson-card for tutors.

}}
sendingMessage={sendingMessageLessonId === item.lesson.id}
disabled={!!sendingMessageLessonId}
Expand Down
25 changes: 25 additions & 0 deletions packages/ui/src/components/Lessons/LessonCard.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Meta, StoryObj } from "@storybook/react";
import LessonCard, { Props } from "@/components/Lessons/LessonCard";
import dayjs from "@/lib/dayjs";
import { faker } from "@faker-js/faker/locale/ar";
import { range } from "lodash";

const meta: Meta<Props> = {
title: "Lessons/LessonCard",
Expand Down Expand Up @@ -30,6 +31,8 @@ export const BeforeJoinForStudent: Story = {
name: faker.person.fullName(),
image: url,
role: "tutor",
topics: range(7).map(() => faker.lorem.words(1)),
level: "C1",
},
...actions,
},
Expand All @@ -45,6 +48,8 @@ export const BeforeJoinForTutor: Story = {
name: faker.person.fullName(),
image: url,
role: "student",
topics: range(7).map(() => faker.lorem.words(1)),
level: "C1",
},
...actions,
},
Expand All @@ -60,6 +65,8 @@ export const CanJoinLesson: Story = {
name: faker.person.fullName(),
image: url,
role: "tutor",
topics: range(7).map(() => faker.lorem.words(1)),
level: "C1",
},
...actions,
},
Expand All @@ -75,6 +82,8 @@ export const CanJoinLessonNow: Story = {
name: faker.person.fullName(),
image: url,
role: "tutor",
topics: range(7).map(() => faker.lorem.words(1)),
level: "C1",
},
...actions,
},
Expand All @@ -90,6 +99,8 @@ export const AfterLessonStarted: Story = {
name: faker.person.fullName(),
image: url,
role: "tutor",
topics: range(7).map(() => faker.lorem.words(1)),
level: "C1",
},
...actions,
},
Expand All @@ -105,6 +116,8 @@ export const LessonAboutToEnd: Story = {
name: faker.person.fullName(),
image: url,
role: "tutor",
topics: range(7).map(() => faker.lorem.words(1)),
level: "C1",
},
...actions,
},
Expand All @@ -120,6 +133,8 @@ export const AfterLessonFinishForStudent: Story = {
name: faker.person.fullName(),
image: url,
role: "tutor",
topics: range(7).map(() => faker.lorem.words(1)),
level: "C1",
},
...actions,
},
Expand All @@ -135,6 +150,8 @@ export const AfterLessonFinishForTutor: Story = {
name: faker.person.fullName(),
image: url,
role: "student",
topics: range(7).map(() => faker.lorem.words(1)),
level: "C1",
},
...actions,
},
Expand All @@ -150,6 +167,8 @@ export const CanceledByTutor: Story = {
name: faker.person.fullName(),
image: url,
role: "tutor",
topics: range(7).map(() => faker.lorem.words(1)),
level: "C1",
},
...actions,
},
Expand All @@ -165,6 +184,8 @@ export const CanceledByStudent: Story = {
name: faker.person.fullName(),
image: url,
role: "student",
topics: range(7).map(() => faker.lorem.words(1)),
level: "C1",
},
...actions,
},
Expand All @@ -180,6 +201,8 @@ export const CanceledByCurrentTutor: Story = {
name: faker.person.fullName(),
image: url,
role: "student",
topics: range(7).map(() => faker.lorem.words(1)),
level: "C1",
},
...actions,
},
Expand All @@ -195,6 +218,8 @@ export const CanceledByCurrentStudent: Story = {
name: faker.person.fullName(),
image: url,
role: "tutor",
topics: range(7).map(() => faker.lorem.words(1)),
level: "C1",
},
...actions,
},
Expand Down
149 changes: 118 additions & 31 deletions packages/ui/src/components/Lessons/LessonCard.tsx
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You better write this in LessonCardV2 for instance.

Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ import { Menu, type MenuAction } from "@/components/Menu";
import CalendarEdit from "@litespace/assets/CalendarEdit";
import CalendarRemove from "@litespace/assets/CalendarRemove";
import CheckCircle from "@litespace/assets/CheckCircle";
import Calendar from "@litespace/assets/Calendar";
import AllMessages from "@litespace/assets/AllMessages";
import Clock from "@litespace/assets/Clock";

export type Props = {
start: string;
Expand All @@ -24,7 +27,10 @@ export type Props = {
* @note role shall be `student` when the current user is tutor and vice versa
*/
role: "tutor" | "student";
topics: Array<string>;
level: string;
};

sendingMessage: boolean;
disabled: boolean;
onRebook: Void;
Expand Down Expand Up @@ -126,6 +132,16 @@ export const LessonCard: React.FC<Props> = ({
return () => clearInterval(interval);
}, [getTitle]);

const isEnded = useMemo(() => end.isBefore(dayjs()), [end]);
const isOngoing = useMemo(
() => !canceled && dayjs().isBetween(dayjs(start), end),
[canceled, start, end]
);
const isFuture = useMemo(
() => !canceled && dayjs().isBefore(dayjs(start)),
[canceled, start]
);

const actions: MenuAction[] = useMemo(
() =>
currentUserRole === "student"
Expand Down Expand Up @@ -154,25 +170,41 @@ export const LessonCard: React.FC<Props> = ({
);

const action = useMemo(() => {
const ended = end.isBefore(dayjs());
if (currentUserRole === "student" && (ended || canceled))
return {
label: intl("lessons.button.rebook"),
onClick: onRebook,
};
if (currentUserRole === "tutor" && (ended || canceled))
return {
label: intl("lessons.button.send-message"),
onClick: onSendMsg,
};
if (currentUserRole === "student") {
if (isEnded || canceled) {
return {
label: intl("lessons.button.rebook"),
onClick: onRebook,
};
}
} else if (currentUserRole === "tutor") {
if (isEnded) {
return {
label: intl("lessons.button.send-message"),
onClick: onSendMsg,
};
}
if (canceled) {
return {
label: intl("lessons.button.contact-student"),
onClick: onSendMsg,
};
}
}
return {
label: intl("lessons.button.join"),
onClick: onJoin,
};
}, [canceled, currentUserRole, end, intl, onJoin, onRebook, onSendMsg]);
}, [canceled, currentUserRole, isEnded, intl, onJoin, onRebook, onSendMsg]);

const buttonType = useMemo(
() => (currentUserRole === "tutor" && isOngoing ? "main" : "natural"),
[currentUserRole, isOngoing]
);

const button = (
<Button
type={buttonType}
size="large"
className={cn("w-full mt-auto")}
disabled={disabled}
Expand All @@ -181,7 +213,10 @@ export const LessonCard: React.FC<Props> = ({
>
<Typography
tag="span"
className="text-natural-50 text-caption font-semibold"
className={cn(
"text-caption font-semibold",
isOngoing ? "text-natural-50" : "text-natural-700"
)}
>
{action.label}
</Typography>
Expand Down Expand Up @@ -229,33 +264,85 @@ export const LessonCard: React.FC<Props> = ({
</div>
<div className="flex flex-col gap-6">
<div className="flex gap-2">
<div className="w-[65px] h-[65px] rounded-full overflow-hidden">
<AvatarV2 src={member.image} alt={member.name} id={member.id} />
<div className="relative min-w-[74px] min-h-[74px] aspect-square shrink-0">
<div
className={cn(
"absolute w-[19px] h-[19px] right-0 top-0 p-0.5 bg-success-700 rounded-full z-10",
"flex items-center justify-center"
)}
>
<Typography tag="span" className="text-natural-0 text-[10px]">
{member.level}
</Typography>
</div>
<div className="w-full h-full rounded-full overflow-hidden">
<AvatarV2 src={member.image} alt={member.name} id={member.id} />
</div>
</div>
<div className="flex flex-col gap-1">
<div className="flex w-full flex-col gap-1">
<Typography
tag="span"
className="text-caption font-bold leading-[21px] text-natural-950"
>
{member.name}
</Typography>
<Typography
tag="span"
className="text-natural-700 text-tiny font-normal"
>
{dayjs(start).format("dddd، D MMMM")}
</Typography>
<Typography
tag="span"
className="text-natural-700 flex items-center text-tiny font-normal"
>
{dayjs(start).format("h:mm a")}
{" - "}
{dayjs(start).add(duration, "minutes").format("h:mm a")}
</Typography>

<div className="flex items-center justify-between">
<div className="flex items-center gap-1">
<Clock className="w-3.5 h-3.5" />
<Typography
tag="span"
className="text-natural-700 flex items-center text-tiny font-normal"
>
{dayjs(start).format("h:mm a")}
{" - "}
{dayjs(start).add(duration, "minutes").format("h:mm a")}
</Typography>
</div>

<div className="flex items-center gap-1">
<Calendar className="w-3.5 h-3.5" />
<Typography
tag="span"
className="text-natural-700 text-tiny font-normal"
>
{dayjs(start).format("dddd، D MMMM")}
</Typography>
</div>
</div>

<div className="mt-2 flex items-center gap-1">
{member.topics.slice(0, 4).map((topic) => (
<Typography
key={topic}
tag="span"
className="border border-natural-500 rounded-[200px] px-1.5 py-1"
>
{topic}
</Typography>
))}
{member.topics.length > 4 && (
<Typography
key="remaining"
tag="span"
className="border border-natural-500 rounded-[200px] px-1.5 py-1"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

specify the text color, size, and width to match the prototype.

>
{member.topics.length - 4}+
</Typography>
)}
</div>
</div>
</div>
{button}
<div className="flex gap-4">
{button}
{currentUserRole === "tutor" && isFuture && (
<Button
startIcon={<AllMessages className="icon w-4 h-4" />}
type="natural"
size="large"
/>
)}
</div>
</div>
</div>
);
Expand Down
3 changes: 2 additions & 1 deletion packages/ui/src/locales/ar-eg.json
Original file line number Diff line number Diff line change
Expand Up @@ -732,6 +732,7 @@
"lessons.button.rebook": "إعادة الحجز",
"lessons.button.join": "دخول الحصة",
"lessons.button.send-message": "إرسال رسالة",
"lessons.button.contact-student": "تواصل مع الطالب",
"lessons.button.find-tutors": "تصفح المعلمين",
"lessons.canceled-by-tutor": "لقد تم إلغاء الحصة من قبل المعلم",
"lessons.canceled-by-student": "لقد قام الطالب بإلغاء الحصة",
Expand Down Expand Up @@ -779,7 +780,7 @@
"student-dashboard.past-lessons.loading": "برجاء الانتظار... جاري تحميل الدروس السابقة!",
"student-dashboard.past-lessons.error": "عذرًا، حدث خطأ أثناء تحميل الدروس السابقة، برجاء المحاولة مرة أخرى",
"tour.sdashboard/1.title": "👋 مرحبًا بك في رحلتك لتعلّم اللغة!",
"tour.sdashboard/1.description": "من خلال صفحة المدرّسين، تستطيع ان تستعرض كل المعلمين وتقوم بـ حجز جلسة مباشرة مع أيّ معلم منهم.كل ما عليك هو ان تضغط على المدرّس المناسب لك، وتستعرض ملفه الشخصي، مواعيده المتاحة، وتبدأ الحجز في الحال!",
"tour.sdashboard/1.description": "من خلال صفحة المدرّسين، تستطيع ان تستعرض كل المعلمين وتقوم بـ حجز جلسة مباشرة مع أيّ معلم منهم.كل ما عليك هو ان تضغط على المدرّس المناسب لك، وتستعرض ملفه الشخصي، مواعيده المتاحة، وتبدأ الحجز في الحال!",
"tour.sdashboard/2.title": "هل وجدت المعلم المناسب لك؟",
"tour.sdashboard/2.description": "اضغط على 'احجز الآن' لكي تبدأ أول جلسة مع المعلم.",
"tour.sdashboard/3.title": "الفيديو التعريفي للمعلم",
Expand Down
Loading