chore: contact us module
This commit is contained in:
@@ -11,6 +11,7 @@ import { databaseConfigs } from "./configs/typeorm.config";
|
|||||||
import { HTTPLogger } from "./core/middlewares/logger.middleware";
|
import { HTTPLogger } from "./core/middlewares/logger.middleware";
|
||||||
import { AnnouncementsModule } from "./modules/announcements/announcement.module";
|
import { AnnouncementsModule } from "./modules/announcements/announcement.module";
|
||||||
import { AuthModule } from "./modules/auth/auth.module";
|
import { AuthModule } from "./modules/auth/auth.module";
|
||||||
|
import { ContactUsModule } from "./modules/contact-us/contact-us.module";
|
||||||
import { CriticismModule } from "./modules/criticisms/criticisms.module";
|
import { CriticismModule } from "./modules/criticisms/criticisms.module";
|
||||||
import { DanakServicesModule } from "./modules/danak-services/danak-services.module";
|
import { DanakServicesModule } from "./modules/danak-services/danak-services.module";
|
||||||
import { SettingModule } from "./modules/settings/settings.module";
|
import { SettingModule } from "./modules/settings/settings.module";
|
||||||
@@ -33,6 +34,7 @@ import { UsersModule } from "./modules/users/users.module";
|
|||||||
UploaderModule,
|
UploaderModule,
|
||||||
CriticismModule,
|
CriticismModule,
|
||||||
SettingModule,
|
SettingModule,
|
||||||
|
ContactUsModule,
|
||||||
],
|
],
|
||||||
controllers: [],
|
controllers: [],
|
||||||
providers: [],
|
providers: [],
|
||||||
|
|||||||
@@ -187,3 +187,15 @@ export const enum SettingMessageEnum {
|
|||||||
SMS_MUST_BE_BOOLEAN = "تنظیمات اسمس باید از نوع BOOLEAN باشد",
|
SMS_MUST_BE_BOOLEAN = "تنظیمات اسمس باید از نوع BOOLEAN باشد",
|
||||||
EMAIL_MUST_BE_BOOLEAN = "تنظیمات ایمیل باید از نوع BOOLEAN باشد",
|
EMAIL_MUST_BE_BOOLEAN = "تنظیمات ایمیل باید از نوع BOOLEAN باشد",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const enum ContactUsMessage {
|
||||||
|
FULLNAME_IS_REQUIRED = "نام کامل مورد نیاز است",
|
||||||
|
FULLNAME_STRING = "نام کامل باید یک رشته باشد",
|
||||||
|
EMAIL_IS_REQUIRED = "ایمیل مورد نیاز است",
|
||||||
|
INVALID_EMAIL_FORMAT = "فرمت ایمیل وارد شده صحیح نیست",
|
||||||
|
CONTENT_IS_REQUIRED = "محتوا مورد نیاز است",
|
||||||
|
CONTENT_STRING = "محتوا باید یک رشته باشد",
|
||||||
|
TITLE_IS_REQUIRED = "عنوان مورد نیاز است",
|
||||||
|
TITLE_STRING = "عنوان باید یک رشته باشد",
|
||||||
|
NOT_FOUND = "پیامی با این ایدی یافت نشد",
|
||||||
|
}
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ export class AuthController {
|
|||||||
@AuthGuards()
|
@AuthGuards()
|
||||||
@ApiOperation({ summary: "Update user password" })
|
@ApiOperation({ summary: "Update user password" })
|
||||||
@HttpCode(HttpStatus.OK)
|
@HttpCode(HttpStatus.OK)
|
||||||
@Patch("password")
|
@Patch("change-password")
|
||||||
updatePassword(@UserDec() user: User, @Body() updatePasswordDto: UpdatePasswordDto) {
|
updatePassword(@UserDec() user: User, @Body() updatePasswordDto: UpdatePasswordDto) {
|
||||||
return this.authService.updatePassword(user.id, updatePasswordDto);
|
return this.authService.updatePassword(user.id, updatePasswordDto);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,16 @@
|
|||||||
|
import { ApiProperty } from "@nestjs/swagger";
|
||||||
|
import { IsNotEmpty, IsString } from "class-validator";
|
||||||
|
|
||||||
|
import { ContactUsMessage } from "../../../common/enums/message.enum";
|
||||||
|
|
||||||
|
export class CreateContactUsDto {
|
||||||
|
@IsNotEmpty({ message: ContactUsMessage.TITLE_IS_REQUIRED })
|
||||||
|
@IsString({ message: ContactUsMessage.TITLE_STRING })
|
||||||
|
@ApiProperty({ description: "title of contact us", example: "عنوان پیام" })
|
||||||
|
title: string;
|
||||||
|
|
||||||
|
@IsNotEmpty({ message: ContactUsMessage.CONTENT_IS_REQUIRED })
|
||||||
|
@IsString({ message: ContactUsMessage.CONTENT_STRING })
|
||||||
|
@ApiProperty({ description: "content of contact us", example: "متن پیام" })
|
||||||
|
content: string;
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
import { ApiPropertyOptional } from "@nestjs/swagger";
|
||||||
|
import { IsDateString, IsOptional, IsString } from "class-validator";
|
||||||
|
|
||||||
|
import { PaginationDto } from "../../../common/DTO/pagination.dto";
|
||||||
|
import { CriticismMessage } from "../../../common/enums/message.enum";
|
||||||
|
|
||||||
|
export class SearchContactUsQueryDto extends PaginationDto {
|
||||||
|
@IsOptional()
|
||||||
|
@IsDateString()
|
||||||
|
@ApiPropertyOptional({ description: "since query", example: "2025-01-27" })
|
||||||
|
since?: string;
|
||||||
|
|
||||||
|
@IsOptional()
|
||||||
|
@IsString({ message: CriticismMessage.SEARCH_QUERY_STRING })
|
||||||
|
@ApiPropertyOptional({ description: "Search query", example: "search query" })
|
||||||
|
q?: string;
|
||||||
|
}
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
import { Body, Controller, Get, Param, Post, Query } from "@nestjs/common";
|
||||||
|
import { ApiOperation, ApiTags } from "@nestjs/swagger";
|
||||||
|
|
||||||
|
import { CreateContactUsDto } from "./DTO/create-contact-us.dto";
|
||||||
|
import { SearchContactUsQueryDto } from "./DTO/search-contact-us-query.dto";
|
||||||
|
import { ContactUsService } from "./providers/contact-us.service";
|
||||||
|
import { AuthGuards } from "../../common/decorators/auth-guard.decorator";
|
||||||
|
import { Roles } from "../../common/decorators/roles.decorator";
|
||||||
|
import { UserDec } from "../../common/decorators/user.decorator";
|
||||||
|
import { ParamDto } from "../../common/DTO/param.dto";
|
||||||
|
import { User } from "../users/entities/user.entity";
|
||||||
|
import { RoleEnum } from "../users/enums/role.enum";
|
||||||
|
|
||||||
|
@Controller("contact-us")
|
||||||
|
@ApiTags("ContactUs")
|
||||||
|
export class ContactUsController {
|
||||||
|
constructor(private readonly contactUsService: ContactUsService) {}
|
||||||
|
|
||||||
|
@AuthGuards()
|
||||||
|
@ApiOperation({ summary: "Create a new contact us" })
|
||||||
|
@Post()
|
||||||
|
createContactUs(@UserDec() user: User, @Body() createContactUsDto: CreateContactUsDto) {
|
||||||
|
return this.contactUsService.createContactUs(user.id, createContactUsDto);
|
||||||
|
}
|
||||||
|
|
||||||
|
//******************** */
|
||||||
|
|
||||||
|
@AuthGuards()
|
||||||
|
@Roles(RoleEnum.ADMIN)
|
||||||
|
@ApiOperation({ summary: "Get all contact us ===> login as admin" })
|
||||||
|
@Get()
|
||||||
|
getAllContactUs(@Query() queryDto: SearchContactUsQueryDto) {
|
||||||
|
return this.contactUsService.getAllContactUs(queryDto);
|
||||||
|
}
|
||||||
|
|
||||||
|
//******************** */
|
||||||
|
|
||||||
|
@AuthGuards()
|
||||||
|
@Roles(RoleEnum.ADMIN)
|
||||||
|
@ApiOperation({ summary: "Get one contact us with id ===> login as admin" })
|
||||||
|
@Get(":id")
|
||||||
|
getOneContactUs(@Param() paramDto: ParamDto) {
|
||||||
|
return this.contactUsService.getOneContactUs(paramDto.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
import { Module } from "@nestjs/common";
|
||||||
|
import { TypeOrmModule } from "@nestjs/typeorm";
|
||||||
|
|
||||||
|
import { ContactUsController } from "./contact-us.controller";
|
||||||
|
import { ContactUs } from "./entities/contact-us.entity";
|
||||||
|
import { ContactUsService } from "./providers/contact-us.service";
|
||||||
|
import { UsersModule } from "../users/users.module";
|
||||||
|
import { ContactUsRepository } from "./repositories/contact-us.repository";
|
||||||
|
|
||||||
|
@Module({
|
||||||
|
imports: [TypeOrmModule.forFeature([ContactUs]), UsersModule],
|
||||||
|
providers: [ContactUsRepository, ContactUsService],
|
||||||
|
controllers: [ContactUsController],
|
||||||
|
exports: [],
|
||||||
|
})
|
||||||
|
export class ContactUsModule {}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
import { Column, Entity, ManyToOne } from "typeorm";
|
||||||
|
|
||||||
|
import { BaseEntity } from "../../../common/entities/base.entity";
|
||||||
|
import { User } from "../../users/entities/user.entity";
|
||||||
|
|
||||||
|
@Entity()
|
||||||
|
export class ContactUs extends BaseEntity {
|
||||||
|
@Column({ type: "int", generated: "identity", insert: false })
|
||||||
|
numericId: number;
|
||||||
|
|
||||||
|
@Column({ type: "varchar", length: 150, nullable: false })
|
||||||
|
title: string;
|
||||||
|
|
||||||
|
@Column({ type: "text", nullable: false })
|
||||||
|
content: string;
|
||||||
|
|
||||||
|
@ManyToOne(() => User, (user) => user.contactUs, { nullable: false, onDelete: "CASCADE" })
|
||||||
|
user: User;
|
||||||
|
|
||||||
|
@Column({ type: "boolean", default: false })
|
||||||
|
isRead: boolean;
|
||||||
|
}
|
||||||
@@ -0,0 +1,89 @@
|
|||||||
|
import { BadRequestException, Injectable } from "@nestjs/common";
|
||||||
|
|
||||||
|
import { CommonMessage, ContactUsMessage } from "../../../common/enums/message.enum";
|
||||||
|
import { UsersService } from "../../users/providers/users.service";
|
||||||
|
import { PaginationUtils } from "../../utils/providers/pagination.utils";
|
||||||
|
import { CreateContactUsDto } from "../DTO/create-contact-us.dto";
|
||||||
|
import { SearchContactUsQueryDto } from "../DTO/search-contact-us-query.dto";
|
||||||
|
import { ContactUsRepository } from "../repositories/contact-us.repository";
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class ContactUsService {
|
||||||
|
constructor(
|
||||||
|
private readonly contactUsRepository: ContactUsRepository,
|
||||||
|
private readonly usersService: UsersService,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
//****************************** */
|
||||||
|
|
||||||
|
async createContactUs(userId: string, createContactUsDto: CreateContactUsDto) {
|
||||||
|
const { user } = await this.usersService.findOneById(userId);
|
||||||
|
|
||||||
|
const contactUs = this.contactUsRepository.create({
|
||||||
|
...createContactUsDto,
|
||||||
|
user,
|
||||||
|
});
|
||||||
|
await this.contactUsRepository.save(contactUs);
|
||||||
|
|
||||||
|
return {
|
||||||
|
message: CommonMessage.CREATED,
|
||||||
|
contactUs,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
//****************************** */
|
||||||
|
|
||||||
|
async getAllContactUs(queryDto: SearchContactUsQueryDto) {
|
||||||
|
const { limit, skip } = PaginationUtils(queryDto);
|
||||||
|
|
||||||
|
const queryBuilder = this.contactUsRepository
|
||||||
|
.createQueryBuilder("contactus")
|
||||||
|
.leftJoinAndSelect("contactus.user", "user")
|
||||||
|
.select([
|
||||||
|
"contactus.id",
|
||||||
|
"contactus.numericId",
|
||||||
|
"contactus.title",
|
||||||
|
"contactus.content",
|
||||||
|
"contactus.isRead",
|
||||||
|
"contactus.createdAt",
|
||||||
|
"user.id",
|
||||||
|
"user.email",
|
||||||
|
"user.firstName",
|
||||||
|
"user.lastName",
|
||||||
|
]);
|
||||||
|
|
||||||
|
if (queryDto.since) {
|
||||||
|
queryBuilder.andWhere("contactus.createdAt >= :since", { since: queryDto.since });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (queryDto.q) {
|
||||||
|
queryBuilder.andWhere(
|
||||||
|
"(contactus.title LIKE :q OR contactus.content LIKE :q OR user.email LIKE :q OR user.firstName LIKE :q OR user.lastName LIKE :q)",
|
||||||
|
{
|
||||||
|
q: `%${queryDto.q}%`,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
queryBuilder.orderBy("contactus.createdAt", "DESC");
|
||||||
|
|
||||||
|
const [contactUs, count] = await queryBuilder.skip(skip).take(limit).getManyAndCount();
|
||||||
|
|
||||||
|
return {
|
||||||
|
contactUs,
|
||||||
|
count,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
//****************************** */
|
||||||
|
|
||||||
|
async getOneContactUs(id: string) {
|
||||||
|
const contactUs = await this.contactUsRepository.getOneContactUs(id);
|
||||||
|
|
||||||
|
if (!contactUs) throw new BadRequestException(ContactUsMessage.NOT_FOUND);
|
||||||
|
|
||||||
|
contactUs.isRead = true;
|
||||||
|
await this.contactUsRepository.save(contactUs);
|
||||||
|
|
||||||
|
return { contactUs };
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
import { Injectable } from "@nestjs/common";
|
||||||
|
import { InjectRepository } from "@nestjs/typeorm";
|
||||||
|
import { Repository } from "typeorm";
|
||||||
|
|
||||||
|
import { ContactUs } from "../entities/contact-us.entity";
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class ContactUsRepository extends Repository<ContactUs> {
|
||||||
|
constructor(@InjectRepository(ContactUs) contactUsRepository: Repository<ContactUs>) {
|
||||||
|
super(contactUsRepository.target, contactUsRepository.manager, contactUsRepository.queryRunner);
|
||||||
|
}
|
||||||
|
|
||||||
|
async getOneContactUs(id: string) {
|
||||||
|
return await this.findOne({
|
||||||
|
where: {
|
||||||
|
id,
|
||||||
|
},
|
||||||
|
relations: ["user"],
|
||||||
|
select: {
|
||||||
|
id: true,
|
||||||
|
numericId: true,
|
||||||
|
title: true,
|
||||||
|
content: true,
|
||||||
|
isRead: true,
|
||||||
|
createdAt: true,
|
||||||
|
user: {
|
||||||
|
id: true,
|
||||||
|
email: true,
|
||||||
|
firstName: true,
|
||||||
|
lastName: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,6 +5,7 @@ import { Role } from "./role.entity";
|
|||||||
import { UserGroup } from "./user-group.entity";
|
import { UserGroup } from "./user-group.entity";
|
||||||
import { BaseEntity } from "../../../common/entities/base.entity";
|
import { BaseEntity } from "../../../common/entities/base.entity";
|
||||||
import { UserAnnouncement } from "../../announcements/entities/user-announcement.entity";
|
import { UserAnnouncement } from "../../announcements/entities/user-announcement.entity";
|
||||||
|
import { ContactUs } from "../../contact-us/entities/contact-us.entity";
|
||||||
import { Criticism } from "../../criticisms/entities/criticism.entity";
|
import { Criticism } from "../../criticisms/entities/criticism.entity";
|
||||||
import { DanakService } from "../../danak-services/entities/danak-service.entity";
|
import { DanakService } from "../../danak-services/entities/danak-service.entity";
|
||||||
import { UserSetting } from "../../settings/entities/user-setting.entity";
|
import { UserSetting } from "../../settings/entities/user-setting.entity";
|
||||||
@@ -53,6 +54,9 @@ export class User extends BaseEntity {
|
|||||||
@OneToMany(() => Criticism, (criticism) => criticism.user)
|
@OneToMany(() => Criticism, (criticism) => criticism.user)
|
||||||
criticisms: Criticism[];
|
criticisms: Criticism[];
|
||||||
|
|
||||||
|
@OneToMany(() => ContactUs, (contactUs) => contactUs.user)
|
||||||
|
contactUs: ContactUs[];
|
||||||
|
|
||||||
@ManyToMany(() => DanakService, (danakService) => danakService.users)
|
@ManyToMany(() => DanakService, (danakService) => danakService.users)
|
||||||
@JoinTable()
|
@JoinTable()
|
||||||
danakServices: DanakService[];
|
danakServices: DanakService[];
|
||||||
|
|||||||
Reference in New Issue
Block a user