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
13 changes: 13 additions & 0 deletions packages/common/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ export class User {
photoURL!: string
defaultMessage?: string
sid?: number
namePronunciation?: string
includeDefaultMessage!: boolean
courses!: UserCourse[]
desktopNotifsEnabled!: boolean
Expand Down Expand Up @@ -201,6 +202,10 @@ export class UserPartial {
@IsOptional()
sid?: number

@IsString()
@IsOptional()
namePronunciation?: string

@IsOptional()
@IsString()
TANotes?: string
Expand Down Expand Up @@ -1722,6 +1727,10 @@ export class UpdateProfileParams {
@IsOptional()
email?: string

@IsString()
@IsOptional()
namePronunciation?: string

@IsString()
@IsOptional()
defaultMessage?: string
Expand Down Expand Up @@ -2585,6 +2594,10 @@ export class AccountRegistrationParams {
@IsOptional()
sid?: number

@IsString()
@IsOptional()
namePronunciation?: string

@IsString()
recaptchaToken!: string
}
Expand Down
4 changes: 4 additions & 0 deletions packages/frontend/app/(auth)/register/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,10 @@ export default function RegisterPage(props: {
</Col>
</Row>

<Form.Item label="Name Pronunciation" name="namePronunciation">
<Input allowClear={true} placeholder="Example: uh-LEE-shuh" />
</Form.Item>

<Form.Item
label="Email"
name="email"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,7 @@ const RosterItem: React.FC<{
title={
<span className="mr-0 md:mr-2">
{item.name}
{item.namePronunciation ? ` (${item.namePronunciation})` : ''}
{item.organizationRole == OrganizationRole.ADMIN && (
<Tooltip title={'This user is an organization administrator.'}>
<CrownFilled
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,9 @@ const QuestionCard: React.FC<QuestionCardProps> = ({
<i>{`[${question.location ?? 'Unselected'}] `}</i>
)}
{question.creator.name}
{question.creator.namePronunciation
? ` (${question.creator.namePronunciation})`
: ''}
</div>
)}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,15 +27,30 @@ const EditProfile: React.FC = () => {
newProfile.sid = parseInt(`${newProfile.sid}`, 10) || undefined
if (userInfo.email === updateProfile.email) {
await API.profile
.patch(pick(newProfile, ['firstName', 'lastName', 'sid']))
.patch(
pick(newProfile, [
'firstName',
'lastName',
'sid',
'namePronunciation',
]),
)
.then(() => setUserInfo({ ...userInfo, ...updateProfile }))
.catch((error) => {
const errorMessage = getErrorMessage(error)
message.error('Error updating profile:' + errorMessage)
})
} else {
await API.profile
.patch(pick(newProfile, ['firstName', 'lastName', 'email', 'sid']))
.patch(
pick(newProfile, [
'firstName',
'lastName',
'email',
'sid',
'namePronunciation',
]),
)
.then(() => setUserInfo({ ...userInfo, ...updateProfile }))
.catch((error) => {
const errorMessage = getErrorMessage(error)
Expand All @@ -49,12 +64,20 @@ const EditProfile: React.FC = () => {
firstName: updateProfile.firstName,
lastName: updateProfile.lastName,
sid: updateProfile.sid,
namePronunciation: updateProfile.namePronunciation,
},
}
newProfile.sid = parseInt(`${newProfile.sid}`, 10) || undefined
setUserInfo(newProfile)
await API.profile
.patch(pick(newProfile, ['firstName', 'lastName', 'sid']))
.patch(
pick(newProfile, [
'firstName',
'lastName',
'sid',
'namePronunciation',
]),
)
.catch((error) => {
const errorMessage = getErrorMessage(error)
message.error('Error updating profile:' + errorMessage)
Expand Down Expand Up @@ -147,6 +170,14 @@ const EditProfile: React.FC = () => {
</Form.Item>
</Col>
</Row>

<Row gutter={{ xs: 8, sm: 16, md: 24, lg: 32 }}>
<Col xs={{ span: 24 }} sm={{ span: 12 }}>
<Form.Item label="Name Pronunciation" name="namePronunciation">
<Input placeholder="Example: uh-LEE-shuh" />
</Form.Item>
</Col>
</Row>
</Form>
<Button
key="submit"
Expand Down
17 changes: 17 additions & 0 deletions packages/server/migration/1771818704948-add-name-pronunciation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { MigrationInterface, QueryRunner } from 'typeorm';

export class AddNamePronunciation1771818704948 implements MigrationInterface {
name = 'AddNamePronunciation1771818704948';

public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`ALTER TABLE "user_model" ADD COLUMN IF NOT EXISTS "namePronunciation" text`,
);
}

public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`ALTER TABLE "user_model" DROP COLUMN IF EXISTS "namePronunciation"`,
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ UserModel {
"includeDefaultMessage": true,
"lastName": "Doe",
"name": "John Doe",
"namePronunciation": null,
"organizationUser": OrganizationUserModel {
"id": 1,
"organizationId": 2,
Expand Down Expand Up @@ -44,6 +45,7 @@ UserModel {
"includeDefaultMessage": true,
"lastName": "Doe",
"name": "John Doe",
"namePronunciation": null,
"organizationUser": OrganizationUserModel {
"id": 1,
"organizationId": 2,
Expand Down
30 changes: 30 additions & 0 deletions packages/server/src/auth/auth.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -874,6 +874,36 @@ describe('AuthService', () => {
sid: 2,
},
],
[
400,
'Name pronunciation must be at most 64 characters',
{
recaptchaToken: 'succeed',
firstName: 'first',
lastName: 'last',
email: 'email@example.com',
password: 'abcdef',
confirmPassword: 'abcdef',
organizationId: 1,
sid: 2,
namePronunciation: 'a'.repeat(65),
},
],
[
null,
null,
{
recaptchaToken: 'succeed',
firstName: 'first',
lastName: 'last',
email: 'email@example.com',
password: 'abcdef',
confirmPassword: 'abcdef',
organizationId: 1,
sid: 2,
namePronunciation: 'uh-LEE-shuh',
},
],
])(
'should return %d with %s if params %o',
async (
Expand Down
10 changes: 10 additions & 0 deletions packages/server/src/auth/auth.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -345,6 +345,7 @@ export class AuthService {
const {
firstName,
lastName,
namePronunciation,
email,
password,
confirmPassword,
Expand Down Expand Up @@ -381,6 +382,12 @@ export class AuthService {
.send({ message: 'First and last name must be at most 32 characters' });
}

if (namePronunciation && namePronunciation.trim().length > 64) {
return res
.status(HttpStatus.BAD_REQUEST)
.send({ message: 'Name pronunciation must be at most 64 characters' });
}

if (email.trim().length < 4 || email.trim().length > 64) {
return res
.status(HttpStatus.BAD_REQUEST)
Expand Down Expand Up @@ -692,6 +699,7 @@ export class AuthService {
password,
sid,
organizationId,
namePronunciation,
}: Omit<
AccountRegistrationParams,
'confirmPassword' | 'recaptchaToken'
Expand Down Expand Up @@ -719,6 +727,7 @@ export class AuthService {
firstName,
lastName,
password: hashedPassword,
namePronunciation,
hideInsights: [],
}).save();
} else {
Expand All @@ -728,6 +737,7 @@ export class AuthService {
firstName,
lastName,
password: hashedPassword,
namePronunciation,
sid,
hideInsights: [],
}).save();
Expand Down
4 changes: 3 additions & 1 deletion packages/server/src/course/course.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,7 @@ describe('CourseService', () => {
firstName: 'Tingwei',
lastName: 'Shi',
email: 'tshi@northeastern.edu',
namePronunciation: 'TING-way',
photoURL:
'https://files.slack.com/files-pri/TE565NU79-F02K81U7S13/image_from_ios.jpg',
});
Expand Down Expand Up @@ -359,13 +360,14 @@ describe('CourseService', () => {
]);
});

it('returns name, email, and photoURL for user', async () => {
it('returns name, email, photoURL, and namePronunciation for user', async () => {
const courseId = 1;
search = 'tingwei';
const user = (await service.getUserInfo(courseId, page, pageSize, search))
.users[0] as UserPartial;
expect(user.name).toEqual('Tingwei Shi');
expect(user.email).toEqual('tshi@northeastern.edu');
expect(user.namePronunciation).toEqual('TING-way');
expect(user.photoURL).toEqual(
'https://files.slack.com/files-pri/TE565NU79-F02K81U7S13/image_from_ios.jpg',
);
Expand Down
2 changes: 1 addition & 1 deletion packages/server/src/course/course.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -367,7 +367,7 @@ export class CourseService {
}

const query = `
SELECT user_model.id, user_model.name, user_model."photoURL", user_model.email, user_model.sid, user_course_model."TANotes", organization_user_model."role" as "organizationRole", COUNT(*) OVER () AS total
SELECT user_model.id, user_model.name, user_model."photoURL", user_model.email, user_model.sid, user_course_model."TANotes", user_model."namePronunciation", organization_user_model."role" as "organizationRole", COUNT(*) OVER () AS total
FROM user_course_model
INNER JOIN user_model ON user_model.id = user_course_model."userId"
LEFT JOIN organization_user_model ON user_course_model."userId" = organization_user_model."userId"
Expand Down
7 changes: 6 additions & 1 deletion packages/server/src/profile/profile.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -161,12 +161,17 @@ describe('ProfileService', () => {
describe('updateUserProfile', () => {
it('should update user profile details', async () => {
const user = await UserFactory.create({ email: 'test@ubc.ca' });
const updatedData = { firstName: 'Updated', lastName: 'User' };
const updatedData = {
firstName: 'Updated',
lastName: 'User',
namePronunciation: 'UP-day-tid',
};

await service.updateUserProfile(user, updatedData);

expect(user.firstName).toBe('Updated');
expect(user.lastName).toBe('User');
expect(user.namePronunciation).toBe('UP-day-tid');
});

it('should throw error when updating email for non-legacy account', async () => {
Expand Down
1 change: 1 addition & 0 deletions packages/server/src/profile/profile.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ export class ProfileService {
'sid',
'firstName',
'lastName',
'namePronunciation',
'photoURL',
'defaultMessage',
'includeDefaultMessage',
Expand Down
3 changes: 3 additions & 0 deletions packages/server/src/profile/user.entity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,9 @@ export class UserModel extends BaseEntity {
@Column({ type: 'boolean', default: false })
accountDeactivated: boolean;

@Column({ type: 'text', nullable: true })
namePronunciation: string | null;

@OneToMany(() => UserCourseModel, (ucm) => ucm.user)
@Exclude()
courses: UserCourseModel[];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ ListQuestionsResponse {
"createdAt": 2020-11-02T12:00:00.000Z,
"creator": {
"name": "User Person",
"namePronunciation": null,
"photoURL": "https://example.com",
},
"creatorId": 1,
Expand Down
13 changes: 13 additions & 0 deletions packages/server/src/queue/queue.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,19 @@ describe('QueueService', () => {
);
});

it('includes creator namePronunciation when present', async () => {
const queue = await QueueFactory.create();
const creator = await UserFactory.create({
namePronunciation: 'uh-LEE-shuh',
});
await QuestionFactory.create({ queue, creator });

const response = await service.getQuestions(queue.id);
expect(response.questions[0].creator.namePronunciation).toEqual(
'uh-LEE-shuh',
);
});

it('filters questions by status appropriately', async () => {
const queue = await QueueFactory.create();
await createQuestionsEveryStatus(queue);
Expand Down
2 changes: 2 additions & 0 deletions packages/server/src/queue/queue.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,7 @@ export class QueueService {
creator: {
name: question.creator.name,
photoURL: question.creator.photoURL,
namePronunciation: question.creator.namePronunciation,
},
});

Expand Down Expand Up @@ -256,6 +257,7 @@ export class QueueService {
creator: {
name: question.creator.name,
photoURL: question.creator.photoURL,
namePronunciation: question.creator.namePronunciation,
},
}) as Question;
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ exports[`Profile Integration GET /profile returns the logged-in user profile 1`]
"includeDefaultMessage": true,
"lastName": "Person",
"name": "User Person",
"namePronunciation": null,
"organization": {
"id": 1,
"orgId": 1,
Expand Down