Skip to content

Commit 7eb3883

Browse files
committed
modif: now tutors can register on the app.
1 parent 73c18f0 commit 7eb3883

File tree

7 files changed

+67
-37
lines changed

7 files changed

+67
-37
lines changed

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

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -48,12 +48,7 @@ const LoginForm: React.FC<{
4848
return router.generic({ route, query: omit(query, "redirect") });
4949
}, [searchParams]);
5050

51-
const google = useGoogle({
52-
// TODO: this should cover both tutors and students.
53-
// should be implemented once the tutor onboarding is finalized.
54-
role: IUser.Role.Student,
55-
redirect,
56-
});
51+
const google = useGoogle({ redirect });
5752

5853
// ========== manual login ============
5954
const onSuccess = useCallback(

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ const Content: React.FC = () => {
1818
}, [params.role]);
1919

2020
useEffect(() => {
21-
if (!role) return navigate(Web.Root);
21+
if (!role) return navigate(Web.Login);
2222
}, [navigate, role]);
2323

2424
return (

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

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,7 @@ const RegisterForm: React.FC<{ role?: Role }> = ({ role }) => {
3737
const verifyEmailDialog = useRender();
3838

3939
// ======== Google Registeration ============
40-
const google = useGoogle({
41-
role,
42-
});
40+
const google = useGoogle({ role });
4341

4442
// ========== manual registeration ============
4543
const onSuccess = useCallback(

apps/web/src/hooks/google.ts

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { useApi } from "@litespace/headless/api";
22
import { useUser } from "@litespace/headless/context/user";
33
import { useFormatMessage } from "@litespace/ui/hooks/intl";
44
import { useToast } from "@litespace/ui/Toast";
5-
import { safe } from "@litespace/utils/error";
5+
import { ResponseError, safe } from "@litespace/utils/error";
66
import { IUser } from "@litespace/types";
77
import { useGoogleLogin, useGoogleOneTapLogin } from "@react-oauth/google";
88
import { useCallback, useMemo, useState } from "react";
@@ -46,11 +46,27 @@ export function useGoogle({
4646
api.auth.google({ token, type, role })
4747
);
4848

49-
if (info instanceof Error)
49+
if (info instanceof ResponseError) {
50+
if (info.statusCode === 404) {
51+
return navigate(
52+
router.web({
53+
route: Web.Register,
54+
role: "student",
55+
})
56+
);
57+
}
5058
return toast.error({
5159
title: intl("login.error"),
5260
description: intl(getErrorMessageId(info)),
5361
});
62+
}
63+
64+
if (info instanceof Error) {
65+
return toast.error({
66+
title: intl("login.error"),
67+
description: intl(getErrorMessageId(info)),
68+
});
69+
}
5470

5571
const regularUser = isRegularUser(info.user);
5672
if (info.user && !regularUser) {

services/server/src/handlers/auth.ts

Lines changed: 21 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,16 @@ import {
44
forbidden,
55
noPassword,
66
notfound,
7-
serviceUnavailable,
87
wrongPassword,
98
} from "@/lib/error/api";
109
import { users } from "@litespace/models";
1110
import { NextFunction, Request, Response } from "express";
12-
import { isSamePassword, registerNewStudent, withImageUrl } from "@/lib/user";
11+
import {
12+
isSamePassword,
13+
registerNewStudent,
14+
registerNewTutor,
15+
withImageUrl,
16+
} from "@/lib/user";
1317
import { IUser } from "@litespace/types";
1418
import { email, password, string } from "@/validation/utils";
1519
import { googleConfig, jwtSecret } from "@/constants";
@@ -121,22 +125,21 @@ async function loginWithGoogle(
121125
if (user && role && role !== user.role) return next(bad());
122126
if (user && (!role || role === user.role)) return await success(user);
123127

124-
const register = !user;
125-
if (register && !role) return next(bad());
126-
if (register) {
127-
// TODO: remove this condition once the turor onboarding is finalized.
128-
if (role === IUser.Role.Tutor) return next(serviceUnavailable());
129-
130-
const { user } = await registerNewStudent({
131-
email: data.email,
132-
verifiedEmail: data.verified,
133-
role,
134-
});
135-
136-
return await success(user);
137-
}
138-
139-
return next(notfound.user());
128+
const canRegister = !user && role !== undefined;
129+
if (!canRegister) return next(notfound.user());
130+
131+
const { user: registeredUser } =
132+
role === IUser.Role.Student
133+
? await registerNewStudent({
134+
email: data.email,
135+
verifiedEmail: data.verified,
136+
})
137+
: await registerNewTutor({
138+
email: data.email,
139+
verifiedEmail: data.verified,
140+
});
141+
142+
return await success(registeredUser);
140143
}
141144

142145
async function loginWithAuthToken(

services/server/src/handlers/user.ts

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -186,11 +186,8 @@ export async function create(req: Request, res: Response, next: NextFunction) {
186186
const payload: IUser.CreateApiPayload = createUserPayload.parse(req.body);
187187
const creator = req.user;
188188
const admin = isAdmin(creator);
189-
// both students and tutors can create/register account on the application,
190-
// hover, currently only students can register. (that's temporary)
191-
// TODO: check if its a regular user rather than just a student, once the tutor
192-
// on-boarding is finalized.
193-
if (payload.role !== IUser.Role.Student && !admin) return next(forbidden());
189+
if (!admin && ![IUser.Role.Student, IUser.Role.Tutor].includes(payload.role))
190+
return next(forbidden());
194191

195192
const userObject = await users.findByEmail(payload.email);
196193
if (userObject) return next(exists.user());

services/server/src/lib/user.ts

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import crypto from "node:crypto";
22
import s3 from "@/lib/s3";
33
import { isValidPhone } from "@litespace/utils";
4-
import { knex, users, students } from "@litespace/models";
5-
import { IStudent, IUser } from "@litespace/types";
4+
import { knex, users, students, tutors } from "@litespace/models";
5+
import { IStudent, ITutor, IUser } from "@litespace/types";
66
import { InvalidPhoneNumber, MissingPhoneNumber } from "@/lib/error/local";
77

88
export function hashPassword(password: string): string {
@@ -108,3 +108,24 @@ export async function registerNewStudent(
108108
return { user, student };
109109
});
110110
}
111+
112+
export async function registerNewTutor(
113+
payload: Partial<IUser.CreateApiPayload> &
114+
Partial<ITutor.CreateApiPayload> & { verifiedEmail?: boolean }
115+
) {
116+
return await knex.transaction(async (tx) => {
117+
const user = await users.create(
118+
{
119+
role: IUser.Role.Tutor,
120+
email: payload.email,
121+
password: payload.password ? hashPassword(payload.password) : "",
122+
verifiedEmail: payload.verifiedEmail,
123+
},
124+
tx
125+
);
126+
127+
const tutor = await tutors.create(user.id, tx);
128+
129+
return { user, tutor };
130+
});
131+
}

0 commit comments

Comments
 (0)