Skip to content

Commit 8c329ef

Browse files
committed
update(web): update student settings page design
1 parent 791d0f5 commit 8c329ef

File tree

9 files changed

+187
-23
lines changed

9 files changed

+187
-23
lines changed

apps/web/src/components/Settings/TopicSelection.tsx renamed to apps/web/src/components/Settings/PublicInfo.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import { isTutor as isUserTutor } from "@litespace/utils";
1717
import cn from "classnames";
1818
import AddCircle from "@litespace/assets/AddCircle";
1919

20-
const TopicSelection: React.FC = () => {
20+
const PublicInfo: React.FC = () => {
2121
const intl = useFormatMessage();
2222
const [showDialog, setShowDialog] = useState<boolean>(false);
2323

@@ -400,4 +400,4 @@ const TopicBadge: React.FC<{ onRemove: Void; label: string }> = ({
400400
);
401401
};
402402

403-
export default TopicSelection;
403+
export default PublicInfo;
Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
import { languageLevels } from "@/constants/student";
2+
import { useOnError } from "@/hooks/error";
3+
import { useUser } from "@litespace/headless/context/user";
4+
import { useForm } from "@litespace/headless/form";
5+
import { useUpdateStudent } from "@litespace/headless/student";
6+
import { useInfiniteTopics } from "@litespace/headless/topic";
7+
import { IStudent } from "@litespace/types";
8+
import { Button } from "@litespace/ui/Button";
9+
import { Form } from "@litespace/ui/Form";
10+
import { useFormatMessage } from "@litespace/ui/hooks/intl";
11+
import { useMakeValidators } from "@litespace/ui/hooks/validation";
12+
import { Input } from "@litespace/ui/Input";
13+
import { validateEnglishLevel } from "@litespace/ui/lib/validate";
14+
import { MultiSelect } from "@litespace/ui/MultiSelect";
15+
import { Select } from "@litespace/ui/Select";
16+
import { Textarea } from "@litespace/ui/Textarea";
17+
import { useToast } from "@litespace/ui/Toast";
18+
import { Typography } from "@litespace/ui/Typography";
19+
import React, { useCallback, useMemo } from "react";
20+
21+
type IForm = {
22+
topics: string[];
23+
career: string;
24+
level: IStudent.EnglishLevel;
25+
aim: string;
26+
};
27+
28+
export const StudentPublicInfo: React.FC = () => {
29+
const intl = useFormatMessage();
30+
const toast = useToast();
31+
const { user } = useUser();
32+
33+
const { list: allTopics } = useInfiniteTopics();
34+
35+
const onSuccess = useCallback(() => {
36+
toast.success({
37+
title: intl("student-settings.updated-successfully"),
38+
});
39+
}, [intl, toast]);
40+
41+
const onError = useOnError({
42+
type: "mutation",
43+
handler: ({ messageId }) => {
44+
toast.error({
45+
title: intl("complete-profile.update.error"),
46+
description: intl(messageId),
47+
});
48+
},
49+
});
50+
51+
const updateStudent = useUpdateStudent({ onSuccess, onError });
52+
53+
const validators = useMakeValidators<IForm>({
54+
career: { required: false },
55+
level: { required: false, validate: validateEnglishLevel },
56+
aim: { required: false },
57+
});
58+
59+
const levels = useMemo(
60+
() =>
61+
Object.entries(languageLevels).map(([key, value]) => ({
62+
label: intl(value),
63+
value: Number(key),
64+
})),
65+
[intl]
66+
);
67+
68+
const form = useForm<IForm>({
69+
defaults: {
70+
topics: [],
71+
career: "",
72+
level: IStudent.EnglishLevel.Beginner,
73+
aim: "",
74+
},
75+
validators,
76+
onSubmit(data) {
77+
if (!user) return;
78+
updateStudent.mutate({
79+
id: user?.id,
80+
payload: {
81+
topics: data.topics,
82+
career: data.career,
83+
level: data.level,
84+
aim: data.aim,
85+
},
86+
});
87+
},
88+
});
89+
90+
return (
91+
<div className="w-full">
92+
<Form onSubmit={form.onSubmit} className="flex flex-col gap-6">
93+
<div className="flex flex-col gap-4">
94+
<Typography tag="h5" className="text-subtitle-2 font-bold">
95+
{intl("student-settings.public-info.title")}
96+
</Typography>
97+
<MultiSelect
98+
label={intl("complete-profile.topics.label")}
99+
options={
100+
allTopics?.map((topic) => ({
101+
label: topic.name.ar,
102+
value: topic.name.ar,
103+
})) || []
104+
}
105+
placeholder={intl("complete-profile.topics.placeholder")}
106+
values={form.state.topics}
107+
setValues={(values) => form.set("topics", values)}
108+
/>
109+
110+
<Input
111+
id="career"
112+
name="career"
113+
idleDir="rtl"
114+
value={form.state.career}
115+
inputSize={"large"}
116+
autoComplete="off"
117+
onChange={(e) => form.set("career", e.target.value)}
118+
label={intl("labels.job")}
119+
placeholder={intl("complete-profile.job.placeholder")}
120+
state={form.errors.career ? "error" : undefined}
121+
helper={form.errors.career}
122+
/>
123+
124+
<Select
125+
id="level"
126+
value={form.state.level}
127+
onChange={(value) => form.set("level", value)}
128+
options={levels}
129+
label={intl("complete-profile.level.label")}
130+
placeholder={intl("complete-profile.level.label")}
131+
helper={form.errors.level}
132+
/>
133+
134+
<Textarea
135+
className="min-h-[138px]"
136+
id="aim"
137+
name="aim"
138+
idleDir="rtl"
139+
value={form.state.aim}
140+
label={intl("complete-profile.aim.label")}
141+
placeholder={intl("complete-profile.aim.placeholder")}
142+
state={form.errors.aim ? "error" : undefined}
143+
helper={form.errors.aim}
144+
onChange={({ target }) => form.set("aim", target.value)}
145+
disabled={updateStudent.isPending}
146+
autoComplete="off"
147+
/>
148+
</div>
149+
150+
<Button
151+
size="large"
152+
htmlType="submit"
153+
loading={updateStudent.isPending}
154+
disabled={updateStudent.isPending}
155+
>
156+
{intl("shared-settings.save")}
157+
</Button>
158+
</Form>
159+
</div>
160+
);
161+
};
162+
163+
export default StudentPublicInfo;

apps/web/src/components/StudentSettings/Content.tsx

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,12 @@ import { Tabs } from "@litespace/ui/Tabs";
55
import NotificationSettings from "@/components/Settings/NotificationSettings";
66
import UpdatePassword from "@/components/Settings/UpdatePassword";
77
import PersonalDetails from "@/components/Settings/PersonalDetails";
8-
import TopicSelection from "@/components/Settings/TopicSelection";
98
import UploadPhoto from "@/components/StudentSettings/UploadPhoto";
109
import { IUser } from "@litespace/types";
1110
import { Tab } from "@/components/StudentSettings/types";
1211
import { isPersonalInfoIncomplete } from "@/components/Settings/utils";
1312
import { StudentSettingsTabId } from "@litespace/utils/routes";
13+
import StudentPublicInfo from "@/components/Settings/StudentPublicInfo";
1414

1515
const Content: React.FC<{
1616
tab: StudentSettingsTabId;
@@ -26,6 +26,11 @@ const Content: React.FC<{
2626
label: intl("shared-settings.personal.title"),
2727
important: isPersonalInfoIncomplete(user),
2828
},
29+
{
30+
id: "public-info",
31+
label: intl("student-settings.public-info.title"),
32+
important: false,
33+
},
2934
{
3035
id: "password",
3136
label: intl("shared-settings.password.title"),
@@ -36,11 +41,6 @@ const Content: React.FC<{
3641
label: intl("shared-settings.notification.title"),
3742
important: !user.notificationMethod,
3843
},
39-
{
40-
id: "topics",
41-
label: intl("student-settings.topics.title"),
42-
important: false,
43-
},
4444
];
4545
}, [intl, user]);
4646

@@ -70,6 +70,12 @@ const Content: React.FC<{
7070
/>
7171
) : null}
7272

73+
{tab === "public-info" ? (
74+
<div className="max-w-[530px] grow flex">
75+
<StudentPublicInfo />
76+
</div>
77+
) : null}
78+
7379
{tab === "password" ? <UpdatePassword id={user.id} /> : null}
7480

7581
{tab === "notifications" ? (
@@ -80,12 +86,6 @@ const Content: React.FC<{
8086
notificationMethod={user.notificationMethod}
8187
/>
8288
) : null}
83-
84-
{tab === "topics" ? (
85-
<div className="max-w-[530px] grow flex">
86-
<TopicSelection />
87-
</div>
88-
) : null}
8989
</div>
9090
);
9191
};
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { StudentSettingsTabId } from "@litespace/utils/routes";
22

33
export function isValidTab(tab: string): tab is StudentSettingsTabId {
4-
return ["personal", "password", "notifications", "topics"].includes(tab);
4+
return ["personal", "public-info", "password", "notifications"].includes(tab);
55
}

apps/web/src/components/TutorProfileSettings/Content.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import { useForm } from "@litespace/headless/form";
1616
import { MAX_TUTOR_ABOUT_TEXT_LENGTH } from "@litespace/utils/constants";
1717
import { Textarea } from "@litespace/ui/Textarea";
1818
import { Input } from "@litespace/ui/Input";
19-
import TopicSelection from "@/components/Settings/TopicSelection";
19+
import TopicSelection from "@/components/Settings/PublicInfo";
2020
import { useBlock } from "@litespace/ui/hooks/common";
2121
import { Button } from "@litespace/ui/Button";
2222
import { useMakeValidators } from "@litespace/ui/hooks/validation";

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,5 +118,5 @@
118118
"sharp"
119119
]
120120
},
121-
"packageManager": "pnpm@10.15.1+sha512.34e538c329b5553014ca8e8f4535997f96180a1d0f614339357449935350d924e22f8614682191264ec33d1462ac21561aff97f6bb18065351c162c7e8f6de67"
121+
"packageManager": "pnpm@10.16.1+sha512.0e155aa2629db8672b49e8475da6226aa4bdea85fdcdfdc15350874946d4f3c91faaf64cbdc4a5d1ab8002f473d5c3fcedcd197989cf0390f9badd3c04678706"
122122
}

packages/ui/src/components/Tabs/Tabs.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ export const Tabs = <T extends string>({
2121
setTab: (id: T) => void;
2222
}) => {
2323
return (
24-
<div className="border border-natural-200 rounded-xl flex items-center justify-between w-full">
24+
<div className="border border-natural-200 rounded-xl flex items-center justify-between w-full max-w-[530px]">
2525
{tabs.map(({ id, label, important, disabled }) => (
2626
<Tab
2727
key={id}
@@ -47,7 +47,7 @@ const Tab: React.FC<{
4747
<button
4848
type="button"
4949
className={cn(
50-
"relative px-4 py-[10px] group rounded-xl w-full",
50+
"relative px-[5.5px] md:px-4 py-[10px] group rounded-xl w-full",
5151
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-offset-0 focus-visible:ring-brand-500",
5252
"flex items-center justify-center"
5353
)}
@@ -57,7 +57,7 @@ const Tab: React.FC<{
5757
<Typography
5858
tag="h6"
5959
className={cn(
60-
"relative font-medium text-tiny md:text-body whitespace-nowrap w-fit",
60+
"relative font-normal md:font-medium text-tiny md:text-body whitespace-nowrap w-fit",
6161
active
6262
? "text-brand-700"
6363
: "text-natural-500 group-hover:text-brand-500 group-active:text-brand-700"

packages/ui/src/locales/ar-eg.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -679,7 +679,7 @@
679679
"discourage-dialog-title": "التواصل المرئي يعزز جودة الحصة",
680680
"discourage-dialog-description": "لا ننصحك باغلاق الكاميرا اثناء الحصة لأن هذا يؤثر علي التجربة العامة للحصة",
681681
"student-settings.password.title": "كلمة المرور",
682-
"student-settings.topics.title": "ادارة المفضلات",
682+
"student-settings.public-info.title": "المعلومات العامة",
683683
"student-settings.profile.title": "الإعدادات",
684684
"student-settings.upload.image.change": "تغيير الصورة",
685685
"student-settings.upload.image.clear": "حذف الصورة",
@@ -702,6 +702,7 @@
702702
"tutor-settings.topics.selection-dialog.title": "شارك خبراتك لتجذب الطلاب الذين يشاركونك الشغف!",
703703
"tutor-settings.topics.selection-dialog.description": "حدد المجالات التي تبرع فيها أو تهتم بها، مثل الرياضة أو التكنولوجيا أو علم النفس، لنساعدك في تخصيص تجربة تدريسية تناسب اهتمامات طلابك وتزيد من تفاعلهم وحماسهم للتعلم.",
704704
"student-settings.add-topics-button.label": "قم بأضافة الموضوعات المفضلة الخاصة بك",
705+
"student-settings.updated-successfully": "تم تحديث بيانانك بنجاح",
705706
"tutor-settings.add-topics-button.label": "قم بأضافة مجالات الخبرة الخاصة بك",
706707
"shared-settings.topics.selection-dialog.loading": "برجاء الانتظار... جاري تحميل المفضلات!",
707708
"shared-settings.topics.selection-dialog.loading-error": " عذرًا، حدث خطأ أثناء تحميل المفضلات. برجاء المحاولة مرة أخرى",

packages/utils/src/routes/route.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,6 @@ export enum Dashboard {
6868

6969
export type StudentSettingsTabId =
7070
| "personal"
71+
| "public-info"
7172
| "password"
72-
| "notifications"
73-
| "topics";
73+
| "notifications";

0 commit comments

Comments
 (0)