Skip to content

Commit

Permalink
adding avatar image url
Browse files Browse the repository at this point in the history
  • Loading branch information
andrenormanlang committed Dec 2, 2024
1 parent da3df43 commit e7effd5
Show file tree
Hide file tree
Showing 18 changed files with 860 additions and 473 deletions.
72 changes: 40 additions & 32 deletions apps/api/src/skills/skills.service.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
import { BadRequestException, Injectable, NotFoundException } from '@nestjs/common';
import {
BadRequestException,
Injectable,
NotFoundException,
} from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { Skill } from './entities/skill.entity';
Expand All @@ -12,7 +16,7 @@ export class SkillsService {
constructor(
@InjectRepository(Skill)
private readonly skillsRepository: Repository<Skill>,
private readonly usersService: UsersService, // Inject UsersService
private readonly usersService: UsersService // Inject UsersService
) {}

/* Get all skill titles */
Expand All @@ -33,7 +37,7 @@ export class SkillsService {
if (user.role !== 'freelancer') {
throw new BadRequestException('Only freelancers can add skills');
}

const skill = this.skillsRepository.create({
...createSkillDto,
user: user,
Expand All @@ -52,12 +56,18 @@ export class SkillsService {
where: { id, user: { id: userId } },
});
if (!skill) {
throw new NotFoundException(`Skill with ID ${id} not found for user ${userId}`);
throw new NotFoundException(
`Skill with ID ${id} not found for user ${userId}`
);
}
return skill;
}

async update(userId: string, id: string, updateSkillDto: UpdateSkillDto): Promise<Skill> {
async update(
userId: string,
id: string,
updateSkillDto: UpdateSkillDto
): Promise<Skill> {
const skill = await this.findOne(userId, id);
Object.assign(skill, updateSkillDto);
return this.skillsRepository.save(skill);
Expand All @@ -71,36 +81,34 @@ export class SkillsService {
/* Search users by skill */
async searchUsersBySkill(query: string): Promise<User[]> {
const skills = await this.skillsRepository
.createQueryBuilder('skill') // This line creates a query builder for the skill entity
.leftJoinAndSelect('skill.user', 'user') // This line joins the user of each skill
.leftJoinAndSelect('user.skills', 'userSkills') // This line joins the skills of each user
.where('skill.title ILIKE :query', { query: `%${query}%` }) // This line filters skills by title
.orWhere('skill.description ILIKE :query', { query: `%${query}%` }) // This line filters skills by description
.select([
'skill.id',
'skill.title',
'skill.description',
'user.id',
'user.username',
'user.imageUrls',
'user.email',
'userSkills.id',
'userSkills.title',
'userSkills.description'
]) // This line selects the columns to return
.getMany(); // This line executes the query and returns the results
.createQueryBuilder('skill') // This line creates a query builder for the skill entity
.leftJoinAndSelect('skill.user', 'user') // This line joins the user of each skill
.leftJoinAndSelect('user.skills', 'userSkills') // This line joins the skills of each user
.where('skill.title ILIKE :query', { query: `%${query}%` }) // This line filters skills by title
.orWhere('skill.description ILIKE :query', { query: `%${query}%` }) // This line filters skills by description
.select([
'skill.id',
'skill.title',
'skill.description',
'user.id',
'user.username',
'user.skillImageUrls',
'user.email',
'userSkills.id',
'userSkills.title',
'userSkills.description',
]) // This line selects the columns to return
.getMany(); // This line executes the query and returns the results

console.log('Generated Query:', skills);

const uniqueUsers = new Map<string, User>();
skills.forEach(skill => {
if (skill.user) {
uniqueUsers.set(skill.user.id, skill.user);
}
skills.forEach((skill) => {
if (skill.user) {
uniqueUsers.set(skill.user.id, skill.user);
}
});

return Array.from(uniqueUsers.values());
}


}
20 changes: 16 additions & 4 deletions apps/api/src/users/dto/create-user-controller.dto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,15 +31,27 @@ export class CreateUserControllerDto {
@IsEnum(UserRole, { message: 'Role must be either freelancer or employer' })
role: UserRole;

@ApiPropertyOptional({
type: 'string',
format: 'binary',
description: 'Avatar image file',
})
@IsOptional()
avatarUrl?: string;

@ApiProperty({
example: 'StrongPassword123!',
description: 'The password for the user account',
})
@IsString({ message: 'Password must be a string' })
@MinLength(8, { message: 'Password must be at least 8 characters long' })
@Matches(/^(?=.*[A-Za-z])(?=.*\d)(?=.*[!@#$%^&*()_+[\]{};':"\\|,.<>/?`~])[A-Za-z\d!@#$%^&*()_+[\]{};':"\\|,.<>/?`~]{8,}$/, {
message: 'Password must be at least 8 characters long and contain at least one letter, one number, and one special character',
})
@Matches(
/^(?=.*[A-Za-z])(?=.*\d)(?=.*[!@#$%^&*()_+[\]{};':"\\|,.<>/?`~])[A-Za-z\d!@#$%^&*()_+[\]{};':"\\|,.<>/?`~]{8,}$/,
{
message:
'Password must be at least 8 characters long and contain at least one letter, one number, and one special character',
}
)
password: string;

@ApiPropertyOptional({
Expand All @@ -49,7 +61,7 @@ export class CreateUserControllerDto {
})
@IsOptional()
@IsArray()
imageUrls?: string[];
skillImageUrls?: string[];

@ApiProperty({
example: 'JohnDoe',
Expand Down
23 changes: 18 additions & 5 deletions apps/api/src/users/dto/create-user-service.dto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,13 @@ export class CreateUserDto {
})
@IsString({ message: 'Password must be a string' })
@MinLength(8, { message: 'Password must be at least 8 characters long' })
@Matches(/^(?=.*[A-Za-z])(?=.*\d)(?=.*[!@#$%^&*()_+[\]{};':"\\|,.<>/?`~])[A-Za-z\d!@#$%^&*()_+[\]{};':"\\|,.<>/?`~]{8,}$/, {
message: 'Password must be at least 8 characters long and contain at least one letter, one number, and one special character',
})
@Matches(
/^(?=.*[A-Za-z])(?=.*\d)(?=.*[!@#$%^&*()_+[\]{};':"\\|,.<>/?`~])[A-Za-z\d!@#$%^&*()_+[\]{};':"\\|,.<>/?`~]{8,}$/,
{
message:
'Password must be at least 8 characters long and contain at least one letter, one number, and one special character',
}
)
password: string;

@ApiPropertyOptional({
Expand All @@ -47,9 +51,9 @@ export class CreateUserDto {
type: [String],
})
@IsOptional()
@IsArray({ message: 'imageUrls must be an array of strings' })
@IsArray({ message: 'skillImageUrls must be an array of strings' })
@IsString({ each: true, message: 'Each image URL must be a string' })
imageUrls?: string[];
skillImageUrls?: string[];

@ApiProperty({
example: 'JohnDoe',
Expand All @@ -66,6 +70,15 @@ export class CreateUserDto {
@IsOptional()
isAdmin: boolean = false;


@ApiPropertyOptional({
type: 'string',
format: 'binary',
description: 'Avatar image file',
})
@IsOptional()
avatarUrl?: string;

@ApiProperty({
type: () => SkillDto,
description: 'Skills associated with the user',
Expand Down
7 changes: 5 additions & 2 deletions apps/api/src/users/dto/update-user.dto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,15 @@ import { Type } from 'class-transformer';
import { Skill } from '../../skills/entities/skill.entity';
import { UserRole } from '../enums/user-role.enum';


export class UpdateUserDto {
@IsEmail()
@IsOptional()
email?: string;

@IsString()
@IsOptional()
avatarUrl?: string;

@IsString()
@MinLength(6)
@IsOptional()
Expand All @@ -36,7 +39,7 @@ export class UpdateUserDto {

@IsString({ each: true })
@IsOptional()
imageUrls?: string[];
skillImageUrls?: string[];

@IsBoolean()
@IsOptional()
Expand Down
9 changes: 8 additions & 1 deletion apps/api/src/users/dto/user-response.dto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,13 @@ export class UserResponseDto {
})
email: string;

@ApiProperty({
example: 'https://example.com/avatar.png',
description: "URL to the user's avatar image",
required: false,
})
avatarUrl?: string;

@ApiProperty({
example: 'JohnDoe',
description: 'The username of the user',
Expand All @@ -35,7 +42,7 @@ export class UserResponseDto {
required: false,
type: [String],
})
imageUrls?: string[];
skillImageUrls?: string[];

@ApiProperty({
example: true,
Expand Down
37 changes: 22 additions & 15 deletions apps/api/src/users/entities/user.entity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,22 @@ export class User {
@PrimaryGeneratedColumn('uuid')
id!: string;

@ApiProperty({
example: 'https://example.com/avatar.png',
description: "URL to the user's avatar image",
required: false,
})
@Column({ nullable: true })
avatarUrl?: string;

@ApiProperty({
example: 'user@example.com',
description: 'The email address of the user',
})
@Column({ unique: true })
email!: string;

@ApiHideProperty()
@ApiHideProperty()
@Column({ type: 'varchar', length: 255, nullable: false })
password: string;

Expand Down Expand Up @@ -51,7 +59,7 @@ export class User {
type: [String],
})
@Column('simple-array', { nullable: true })
imageUrls?: string[];
skillImageUrls?: string[];

@ApiProperty({
example: false,
Expand All @@ -76,7 +84,6 @@ export class User {
})
@Column({ default: false })
isOnline: boolean = false;


// @ApiHideProperty()
// @Column({ nullable: true })
Expand All @@ -91,16 +98,16 @@ export class User {
@IsOptional()
skills!: Skill[];

// Relationships for Chat Messages
@OneToMany(() => ChatMessage, (chat) => chat.sender)
sentMessages!: ChatMessage[];
@OneToMany(() => ChatMessage, (chat) => chat.receiver)
receivedMessages!: ChatMessage[];

@OneToMany(() => Room, (room) => room.employer)
createdRooms!: Room[];
@OneToMany(() => Room, (room) => room.freelancer)
assignedRooms!: Room[];
// Relationships for Chat Messages
@OneToMany(() => ChatMessage, (chat) => chat.sender)
sentMessages!: ChatMessage[];

@OneToMany(() => ChatMessage, (chat) => chat.receiver)
receivedMessages!: ChatMessage[];

@OneToMany(() => Room, (room) => room.employer)
createdRooms!: Room[];

@OneToMany(() => Room, (room) => room.freelancer)
assignedRooms!: Room[];
}
Loading

0 comments on commit e7effd5

Please sign in to comment.