Skip to content

Commit bd44ad9

Browse files
Mk/product experiments (#1129)
2 parents d763f5d + 05120ce commit bd44ad9

File tree

14 files changed

+354
-69
lines changed

14 files changed

+354
-69
lines changed

apps/web/src/components/Auth/Register/Form.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,10 @@ const RegisterForm: React.FC<{ role?: Role }> = ({ role }) => {
4545
const onSuccess = useCallback(
4646
async ({ user: info, token }: IUser.RegisterApiResponse) => {
4747
user.set({ user: info, token });
48-
verifyEmailDialog.show();
48+
navigate(Web.CompleteProfile);
49+
// verifyEmailDialog.show();
4950
},
50-
[user, verifyEmailDialog]
51+
[user, navigate]
5152
);
5253

5354
const onError = useOnError({

apps/web/src/components/Plans/Selector.tsx

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { router } from "@/lib/routes";
33
import { IPlan } from "@litespace/types";
44
import { useFormatMessage } from "@litespace/ui/hooks/intl";
55
import { LocalId } from "@litespace/ui/locales";
6-
import { PlanCard } from "@litespace/ui/PlanCard";
6+
import { PlanCard, TrialSessionCard } from "@litespace/ui/PlanCard";
77
import { Tabs } from "@litespace/ui/Tabs";
88
import { nstr, percentage, price } from "@litespace/utils";
99
import { Web } from "@litespace/utils/routes";
@@ -56,23 +56,19 @@ export const Selector: React.FC<{
5656
"plans.card.beginning.description-1",
5757
"plans.card.beginning.description-2",
5858
],
59-
[
60-
"plans.card.advance.description",
61-
"plans.card.advance.description-1",
62-
"plans.card.advance.description-2",
63-
],
59+
["plans.card.advance.description", "plans.card.advance.description-1"],
6460
[
6561
"plans.card.professional.description",
6662
"plans.card.professional.description-1",
67-
"plans.card.professional.description-2",
6863
],
6964
]);
7065

7166
return (
7267
<div className="flex flex-col gap-6 md:gap-[81px] max-w-screen-xl mx-auto">
7368
<PlansPanel period={period} setPeriod={setPeriod} />
7469

75-
<div className="flex flex-wrap items-center md:flex-row justify-items-center justify-center gap-4 lg:gap-6">
70+
<div className="flex flex-wrap items-stretch md:flex-row justify-items-center justify-center gap-4 lg:gap-6">
71+
<TrialSessionCard />
7672
{sortedPlans.map((plan, idx) => {
7773
const index =
7874
idx < description.current.length

apps/web/src/components/Tutors/TutorCard.tsx

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,6 @@ export const TutorCard: React.FC<{
8585
) */}
8686
<AvatarV2 src={image} alt={name} id={tutorId} object="cover" />
8787
</div>
88-
8988
<div className="flex flex-col">
9089
<div className="flex justify-between mb-1">
9190
<Typography
@@ -143,7 +142,6 @@ export const TutorCard: React.FC<{
143142
</div>
144143
</Optional>
145144
</div>
146-
147145
<Button
148146
htmlType="button"
149147
className="w-full mt-auto text-body font-medium"
@@ -155,6 +153,21 @@ export const TutorCard: React.FC<{
155153
>
156154
{intl("tutor-card.book-now")}
157155
</Button>
156+
<Link
157+
to={router.web({
158+
route: Web.TutorProfile,
159+
id: tutorId,
160+
})}
161+
>
162+
<Button
163+
htmlType="button"
164+
className="w-full md:hidden mt-auto text-body font-medium"
165+
variant="secondary"
166+
size={buttonSize}
167+
>
168+
{intl("tutor-card.show-profile")}
169+
</Button>{" "}
170+
</Link>
158171
</div>
159172
</Link>
160173
);

apps/web/src/pages/StudentDashboard.tsx

Lines changed: 143 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,17 @@ import { UpcomingLessons } from "@/components/dashboard/UpcomingLessons";
55
import { SuggestedTutors } from "@/components/dashboard/SuggestedTutors";
66
import { useOnError } from "@/hooks/error";
77
import { router } from "@/lib/routes";
8-
import EmptyStudentDashboard from "@litespace/assets/EmptyStudentDashboard";
98
import { useMediaQuery } from "@litespace/headless/mediaQuery";
109
import { useFindPersonalizedStudentStats } from "@litespace/headless/student";
1110
import { Button } from "@litespace/ui/Button";
1211
import { Loading } from "@litespace/ui/Loading";
13-
import { Typography } from "@litespace/ui/Typography";
1412
import { useFormatMessage } from "@litespace/ui/hooks/intl";
13+
import { Accordion } from "@litespace/ui/Accordion";
1514
import { Web } from "@litespace/utils/routes";
16-
import React, { useMemo } from "react";
15+
import React, { useMemo, useState } from "react";
1716
import { Link } from "react-router-dom";
1817
import { useTutors } from "@litespace/headless/tutor";
18+
import { Tabs } from "@litespace/ui/Tabs";
1919

2020
const StudentDashboard: React.FC = () => {
2121
const mq = useMediaQuery();
@@ -62,6 +62,7 @@ const StudentDashboard: React.FC = () => {
6262
isPending={query.isLoading}
6363
refetch={query.refetch}
6464
/>
65+
<Questions />
6566

6667
<PastLessons />
6768

@@ -90,9 +91,11 @@ const StudentDashboard: React.FC = () => {
9091
isPending={query.isLoading}
9192
refetch={query.refetch}
9293
/>
93-
94+
<LearningApproachMobile />
95+
<div className="flex-1">
96+
<UpcomingLessons />
97+
</div>
9498
<PastLessons />
95-
9699
<SuggestedTutors
97100
tutors={dashboardTutor}
98101
loading={tutors.query.isLoading}
@@ -103,9 +106,6 @@ const StudentDashboard: React.FC = () => {
103106
<div className="flex-1 ">
104107
<ChatSummary />
105108
</div>
106-
<div className="flex-1">
107-
<UpcomingLessons />
108-
</div>
109109
</div>
110110
</div>
111111
) : null}
@@ -115,20 +115,144 @@ const StudentDashboard: React.FC = () => {
115115

116116
const EmptyDashboard: React.FC = () => {
117117
const intl = useFormatMessage();
118+
const { md } = useMediaQuery();
119+
120+
return (
121+
<div className="my-auto md:my-12 flex flex-col gap-3 mx-auto p-4">
122+
{md ? <Questions /> : null}
123+
{!md ? <LearningApproachMobile /> : null}
124+
<Link to={router.web({ route: Web.Tutors, full: true })} tabIndex={-1}>
125+
<Button
126+
className="w-full md:max-w-[250px] mx-auto"
127+
type="main"
128+
size="large"
129+
>
130+
{intl("student-dashboard.empty-state.btn")}
131+
</Button>
132+
</Link>
133+
</div>
134+
);
135+
};
136+
137+
const Questions = () => {
138+
const [tab, setTab] = useState("methodology");
139+
const intl = useFormatMessage();
140+
141+
const tabs = useMemo(
142+
() => [
143+
{ id: "methodology", label: intl("questions.methodology-tab") },
144+
{ id: "benefits", label: intl("questions.benefits-tab") },
145+
],
146+
[intl]
147+
);
148+
149+
return (
150+
<div className="flex flex-col gap-2">
151+
<div className="md:max-w-max md:mx-auto">
152+
<Tabs tabs={tabs} setTab={setTab} tab={tab} />
153+
</div>
154+
<div>
155+
{tab === "methodology" ? <Methodology /> : null}
156+
{tab === "benefits" ? <Benefits /> : null}
157+
</div>
158+
</div>
159+
);
160+
};
161+
162+
const Methodology = () => {
163+
const intl = useFormatMessage();
164+
return (
165+
<div className="px-4 grid gap-4 md:gap-8 md:grid-cols-3">
166+
<div className="border shadow-md rounded-2xl p-4">
167+
<h2 className="text-xl font-semibold mb-2 text-natural-700">
168+
{intl("questions.methodology.card1.title")}
169+
</h2>
170+
<p className="text-natural-600 leading-relaxed text-sm">
171+
{intl("questions.methodology.card1.desc")}
172+
</p>
173+
</div>
174+
175+
<div className="border shadow-md rounded-2xl p-4">
176+
<h2 className="text-xl font-semibold mb-2 text-natural-700">
177+
{intl("questions.methodology.card2.title")}
178+
</h2>
179+
<p className="text-natural-600 leading-relaxed text-sm">
180+
{intl("questions.methodology.card2.desc")}
181+
</p>
182+
</div>
183+
184+
<div className="border shadow-md rounded-2xl p-4">
185+
<h2 className="text-xl font-semibold mb-2 text-natural-700">
186+
{intl("questions.methodology.card3.title")}
187+
</h2>
188+
<p className="text-natural-600 leading-relaxed text-sm">
189+
{intl("questions.methodology.card3.desc")}
190+
</p>
191+
</div>
192+
</div>
193+
);
194+
};
118195

196+
const Benefits = () => {
197+
const intl = useFormatMessage();
119198
return (
120-
<div className="my-auto flex flex-col gap-6 mx-auto">
121-
<EmptyStudentDashboard className="w-[328px] h-[258px] md:w-[433px] md:h-[342px]" />
122-
<div className="flex flex-col gap-4 text-center w-max mx-auto">
123-
<Typography tag="p" className="text-subtitle-2 font-medium">
124-
{intl("student-dashboard.empty-state.desc")}
125-
</Typography>
126-
<Link to={router.web({ route: Web.Tutors, full: true })} tabIndex={-1}>
127-
<Button className="w-full" type="main" size="large">
128-
{intl("student-dashboard.empty-state.btn")}
129-
</Button>
130-
</Link>
199+
<div className="px-4 grid gap-4 md:gap-8 md:grid-cols-3">
200+
<div className="border shadow-md rounded-2xl p-4">
201+
<h2 className="text-xl font-semibold mb-2 text-natural-700">
202+
{intl("questions.benefits.card1.title")}
203+
</h2>
204+
<p className="text-natural-600 leading-relaxed text-sm">
205+
{intl("questions.benefits.card1.desc")}
206+
</p>
207+
</div>
208+
209+
<div className="border shadow-md rounded-2xl p-4">
210+
<h2 className="text-xl font-semibold mb-2 text-natural-700">
211+
{intl("questions.benefits.card2.title")}
212+
</h2>
213+
<p className="text-natural-600 leading-relaxed text-sm">
214+
{intl("questions.benefits.card2.desc")}
215+
</p>
131216
</div>
217+
218+
<div className="border shadow-md rounded-2xl p-4">
219+
<h2 className="text-xl font-semibold mb-2 text-natural-700">
220+
{intl("questions.benefits.card3.title")}
221+
</h2>
222+
<p className="text-natural-600 leading-relaxed text-sm">
223+
{intl("questions.benefits.card3.desc")}
224+
</p>
225+
</div>
226+
</div>
227+
);
228+
};
229+
230+
const LearningApproachMobile = () => {
231+
const intl = useFormatMessage();
232+
233+
const methodologyContent = intl("questions.methodology.mobile-content");
234+
const studentTipsContent = intl("questions.benefits.mobile-content");
235+
236+
const questions = [
237+
{
238+
title: intl("questions.methodology.mobile-title"),
239+
content: methodologyContent,
240+
},
241+
{
242+
title: intl("questions.benefits.mobile-title"),
243+
content: studentTipsContent,
244+
},
245+
];
246+
247+
return (
248+
<div className="block md:hidden">
249+
<Accordion
250+
items={questions.map(({ title, content }, i) => ({
251+
id: i.toString(),
252+
title,
253+
content,
254+
}))}
255+
/>
132256
</div>
133257
);
134258
};

apps/web/src/pages/Tutors.tsx

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import Content from "@/components/Tutors/Content";
33
import { useOnError } from "@/hooks/error";
44
import { useTutors } from "@litespace/headless/tutor";
55
import { useFormatMessage } from "@litespace/ui/hooks/intl";
6+
import { Typography } from "@litespace/ui/Typography";
67
import React from "react";
78

89
const Tutors: React.FC = () => {
@@ -22,7 +23,12 @@ const Tutors: React.FC = () => {
2223
fetching={tutors.query.isFetching && !tutors.query.isLoading}
2324
className="mb-6"
2425
/>
25-
26+
<Typography
27+
tag="p"
28+
className="text-natural-700 -mt-4 mb-4 md:max-w-3xl mx-auto text-center"
29+
>
30+
{intl("tutors.description")}
31+
</Typography>
2632
<Content
2733
tutors={tutors.list}
2834
loading={tutors.query.isLoading}
Lines changed: 7 additions & 0 deletions
Loading

packages/assets/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,7 @@
154154
"./PlaybackSpeed": "./dist/PlaybackSpeed.tsx",
155155
"./ProfileAvatar": "./dist/ProfileAvatar.tsx",
156156
"./Quote": "./dist/Quote.tsx",
157+
"./Question": "./dist/Question.tsx",
157158
"./Rate": "./dist/Rate.tsx",
158159
"./Receipt": "./dist/Receipt.tsx",
159160
"./RecordCircle": "./dist/RecordCircle.tsx",

packages/ui/src/components/Accordion/Accordion.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ export const Accordion: React.FC<{
6565
<div className="overflow-hidden">
6666
<Typography
6767
tag="p"
68-
className="text-tiny sm:text-caption font-medium sm:font-normal pt-2 sm:pt-3 pb-6"
68+
className="text-tiny whitespace-pre-line sm:text-caption font-medium sm:font-normal pt-2 sm:pt-3 pb-6"
6969
>
7070
{item.content}
7171
</Typography>

packages/ui/src/components/Card/PlanCard/PlanCard.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ export const PlanCard: React.FC<{
5151
"md:last:col-span-2 xl:last:col-span-1 md:first:justify-self-end md:[&:nth-of-type(2)]:justify-self-start",
5252
"xl:first:justify-self-auto xl:[&>nth-of-type(2)]:justify-self-auto",
5353
"relative flex flex-col",
54-
"w-full h-full p-[24px] xl:p-[32px]",
54+
"w-full min-h-[360px] md:min-h-[416px] h-full p-[24px] xl:p-[32px]",
5555
"min-w-[323px] max-w-[400px]",
5656
"bg-natural-50 rounded-2xl border border-natural-100"
5757
)}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { TrialSessionCard } from "@/components/Card/TrialSessionCard/TrialSessionCard";
2+
import type { Meta, StoryObj } from "@storybook/react";
3+
4+
const meta: Meta<typeof TrialSessionCard> = {
5+
title: "Components/TrialSessionCard",
6+
component: TrialSessionCard,
7+
tags: ["autodocs"],
8+
argTypes: {
9+
onClick: { action: "clicked" },
10+
},
11+
};
12+
13+
export default meta;
14+
type Story = StoryObj<typeof TrialSessionCard>;
15+
16+
export const Default: Story = {};

0 commit comments

Comments
 (0)