mirror of
https://github.com/HamzaSha1/zod-backend.git
synced 2025-07-15 10:05:21 +00:00
Merge pull request #11 from HamzaSha1/feat/customer-settings
feat: update customer profile picture and notifications settings
This commit is contained in:
@ -3,18 +3,14 @@ import { JwtModule } from '@nestjs/jwt';
|
||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
import { CustomerModule } from '~/customer/customer.module';
|
||||
import { AuthController } from './controllers';
|
||||
import { Device, User, UserNotificationSettings } from './entities';
|
||||
import { Device, User } from './entities';
|
||||
import { DeviceRepository, UserRepository } from './repositories';
|
||||
import { AuthService, DeviceService } from './services';
|
||||
import { UserService } from './services/user.service';
|
||||
import { AccessTokenStrategy } from './strategies';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
TypeOrmModule.forFeature([User, UserNotificationSettings, Device]),
|
||||
JwtModule.register({}),
|
||||
forwardRef(() => CustomerModule),
|
||||
],
|
||||
imports: [TypeOrmModule.forFeature([User, Device]), JwtModule.register({}), forwardRef(() => CustomerModule)],
|
||||
providers: [AuthService, UserRepository, UserService, DeviceService, DeviceRepository, AccessTokenStrategy],
|
||||
controllers: [AuthController],
|
||||
exports: [UserService],
|
||||
|
@ -1,3 +1,2 @@
|
||||
export * from './device.entity';
|
||||
export * from './user-notification-settings.entity';
|
||||
export * from './user.entity';
|
||||
|
@ -3,7 +3,6 @@ import {
|
||||
Column,
|
||||
CreateDateColumn,
|
||||
Entity,
|
||||
JoinColumn,
|
||||
OneToMany,
|
||||
OneToOne,
|
||||
PrimaryGeneratedColumn,
|
||||
@ -11,10 +10,8 @@ import {
|
||||
} from 'typeorm';
|
||||
import { Otp } from '~/common/modules/otp/entities';
|
||||
import { Customer } from '~/customer/entities/customer.entity';
|
||||
import { Document } from '~/document/entities';
|
||||
import { Roles } from '../enums';
|
||||
import { Device } from './device.entity';
|
||||
import { UserNotificationSettings } from './user-notification-settings.entity';
|
||||
|
||||
@Entity('users')
|
||||
export class User extends BaseEntity {
|
||||
@ -48,22 +45,9 @@ export class User extends BaseEntity {
|
||||
@Column('text', { nullable: true, array: true, name: 'roles' })
|
||||
roles!: Roles[];
|
||||
|
||||
@Column('varchar', { name: 'profile_picture_id', nullable: true })
|
||||
profilePictureId!: string;
|
||||
|
||||
@OneToOne(() => Document, (document) => document.user, { cascade: true, nullable: true })
|
||||
@JoinColumn({ name: 'profile_picture_id' })
|
||||
profilePicture!: Document;
|
||||
|
||||
@OneToMany(() => Otp, (otp) => otp.user)
|
||||
otp!: Otp[];
|
||||
|
||||
@OneToOne(() => UserNotificationSettings, (notificationSettings) => notificationSettings.user, {
|
||||
cascade: true,
|
||||
eager: true,
|
||||
})
|
||||
notificationSettings!: UserNotificationSettings;
|
||||
|
||||
@OneToOne(() => Customer, (customer) => customer.user, { cascade: true, eager: true })
|
||||
customer!: Customer;
|
||||
|
||||
|
@ -1,8 +1,7 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { FindOptionsWhere, Repository } from 'typeorm';
|
||||
import { UpdateNotificationsSettingsRequestDto } from '~/customer/dtos/request';
|
||||
import { User, UserNotificationSettings } from '../entities';
|
||||
import { User } from '../entities';
|
||||
|
||||
@Injectable()
|
||||
export class UserRepository {
|
||||
@ -14,7 +13,6 @@ export class UserRepository {
|
||||
phoneNumber: data.phoneNumber,
|
||||
countryCode: data.countryCode,
|
||||
roles: data.roles,
|
||||
notificationSettings: UserNotificationSettings.create(),
|
||||
}),
|
||||
);
|
||||
}
|
||||
@ -23,11 +21,6 @@ export class UserRepository {
|
||||
return this.userRepository.findOne({ where });
|
||||
}
|
||||
|
||||
updateNotificationSettings(user: User, body: UpdateNotificationsSettingsRequestDto) {
|
||||
user.notificationSettings = UserNotificationSettings.create({ ...user.notificationSettings, ...body });
|
||||
return this.userRepository.save(user);
|
||||
}
|
||||
|
||||
update(userId: string, data: Partial<User>) {
|
||||
return this.userRepository.update(userId, data);
|
||||
}
|
||||
@ -35,7 +28,6 @@ export class UserRepository {
|
||||
createUser(data: Partial<User>) {
|
||||
const user = this.userRepository.create({
|
||||
...data,
|
||||
notificationSettings: UserNotificationSettings.create(),
|
||||
});
|
||||
|
||||
return this.userRepository.save(user);
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { BadRequestException, forwardRef, Inject, Injectable } from '@nestjs/common';
|
||||
import { FindOptionsWhere } from 'typeorm';
|
||||
import { UpdateNotificationsSettingsRequestDto } from '~/customer/dtos/request';
|
||||
import { CustomerNotificationSettings } from '~/customer/entities/customer-notification-settings.entity';
|
||||
import { CustomerService } from '~/customer/services';
|
||||
import { Guardian } from '~/guardian/entities/guradian.entity';
|
||||
import { CreateUnverifiedUserRequestDto } from '../dtos/request';
|
||||
@ -15,15 +15,6 @@ export class UserService {
|
||||
@Inject(forwardRef(() => CustomerService)) private readonly customerService: CustomerService,
|
||||
) {}
|
||||
|
||||
async updateNotificationSettings(userId: string, body: UpdateNotificationsSettingsRequestDto) {
|
||||
const user = await this.findUserOrThrow({ id: userId });
|
||||
|
||||
const notificationSettings = (await this.userRepository.updateNotificationSettings(user, body))
|
||||
.notificationSettings;
|
||||
|
||||
return notificationSettings;
|
||||
}
|
||||
|
||||
findUser(where: FindOptionsWhere<User> | FindOptionsWhere<User>[]) {
|
||||
return this.userRepository.findOne(where);
|
||||
}
|
||||
@ -74,6 +65,7 @@ export class UserService {
|
||||
await this.customerService.createCustomer(
|
||||
{
|
||||
guardian: Guardian.create({ id: user.id }),
|
||||
notificationSettings: new CustomerNotificationSettings(),
|
||||
},
|
||||
user,
|
||||
);
|
||||
|
@ -1,9 +1,9 @@
|
||||
import { Body, Controller, Patch, UseGuards } from '@nestjs/common';
|
||||
import { Body, Controller, Get, Patch, UseGuards } from '@nestjs/common';
|
||||
import { ApiBearerAuth, ApiTags } from '@nestjs/swagger';
|
||||
import { Roles } from '~/auth/enums';
|
||||
import { IJwtPayload } from '~/auth/interfaces';
|
||||
import { AllowedRoles, AuthenticatedUser } from '~/common/decorators';
|
||||
import { AccessTokenGuard, RolesGuard } from '~/common/guards';
|
||||
import { AuthenticatedUser } from '~/common/decorators';
|
||||
import { AccessTokenGuard } from '~/common/guards';
|
||||
import { ApiDataResponse } from '~/core/decorators';
|
||||
import { ResponseFactory } from '~/core/utils';
|
||||
import { UpdateCustomerRequestDto, UpdateNotificationsSettingsRequestDto } from '../dtos/request';
|
||||
import { CustomerResponseDto, NotificationSettingsResponseDto } from '../dtos/response';
|
||||
@ -15,9 +15,18 @@ import { CustomerService } from '../services';
|
||||
export class CustomerController {
|
||||
constructor(private readonly customerService: CustomerService) {}
|
||||
|
||||
@Get('/profile')
|
||||
@UseGuards(AccessTokenGuard)
|
||||
@ApiDataResponse(CustomerResponseDto)
|
||||
async getCustomerProfile(@AuthenticatedUser() { sub }: IJwtPayload) {
|
||||
const customer = await this.customerService.findCustomerById(sub);
|
||||
|
||||
return ResponseFactory.data(new CustomerResponseDto(customer));
|
||||
}
|
||||
|
||||
@Patch('')
|
||||
@UseGuards(RolesGuard)
|
||||
@AllowedRoles(Roles.GUARDIAN)
|
||||
@UseGuards(AccessTokenGuard)
|
||||
@ApiDataResponse(CustomerResponseDto)
|
||||
async updateCustomer(@AuthenticatedUser() { sub }: IJwtPayload, @Body() body: UpdateCustomerRequestDto) {
|
||||
const customer = await this.customerService.updateCustomer(sub, body);
|
||||
|
||||
@ -26,6 +35,7 @@ export class CustomerController {
|
||||
|
||||
@Patch('settings/notifications')
|
||||
@UseGuards(AccessTokenGuard)
|
||||
@ApiDataResponse(NotificationSettingsResponseDto)
|
||||
async updateNotificationSettings(
|
||||
@AuthenticatedUser() { sub }: IJwtPayload,
|
||||
@Body() body: UpdateNotificationsSettingsRequestDto,
|
||||
|
@ -3,11 +3,12 @@ import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
import { AuthModule } from '~/auth/auth.module';
|
||||
import { CustomerController } from './controllers';
|
||||
import { Customer } from './entities';
|
||||
import { CustomerNotificationSettings } from './entities/customer-notification-settings.entity';
|
||||
import { CustomerRepository } from './repositories/customer.repository';
|
||||
import { CustomerService } from './services';
|
||||
|
||||
@Module({
|
||||
imports: [TypeOrmModule.forFeature([Customer]), forwardRef(() => AuthModule)],
|
||||
imports: [TypeOrmModule.forFeature([Customer, CustomerNotificationSettings]), forwardRef(() => AuthModule)],
|
||||
controllers: [CustomerController],
|
||||
providers: [CustomerService, CustomerRepository],
|
||||
exports: [CustomerService],
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { IsDateString, IsNotEmpty, IsOptional, IsString } from 'class-validator';
|
||||
import { IsDateString, IsNotEmpty, IsOptional, IsString, IsUUID } from 'class-validator';
|
||||
import { i18nValidationMessage as i18n } from 'nestjs-i18n';
|
||||
import { IsAbove18 } from '~/core/decorators/validations';
|
||||
export class UpdateCustomerRequestDto {
|
||||
@ -26,4 +26,8 @@ export class UpdateCustomerRequestDto {
|
||||
@IsAbove18({ message: i18n('validation.IsAbove18', { path: 'general', property: 'customer.dateOfBirth' }) })
|
||||
@IsOptional()
|
||||
dateOfBirth!: Date;
|
||||
|
||||
@ApiProperty({ example: '123e4567-e89b-12d3-a456-426614174000' })
|
||||
@IsUUID('4', { message: i18n('validation.IsUUID', { path: 'general', property: 'customer.profilePictureId' }) })
|
||||
profilePictureId!: string;
|
||||
}
|
||||
|
@ -1,5 +1,7 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
|
||||
import { Customer } from '~/customer/entities';
|
||||
import { DocumentMetaResponseDto } from '~/document/dtos/response';
|
||||
import { NotificationSettingsResponseDto } from './notification-settings.response.dto';
|
||||
|
||||
export class CustomerResponseDto {
|
||||
@ApiProperty()
|
||||
@ -50,6 +52,12 @@ export class CustomerResponseDto {
|
||||
@ApiProperty()
|
||||
isGuardian!: boolean;
|
||||
|
||||
@ApiProperty()
|
||||
notificationSettings!: NotificationSettingsResponseDto;
|
||||
|
||||
@ApiPropertyOptional({ type: DocumentMetaResponseDto })
|
||||
profilePicture!: DocumentMetaResponseDto | null;
|
||||
|
||||
constructor(customer: Customer) {
|
||||
this.id = customer.id;
|
||||
this.customerStatus = customer.customerStatus;
|
||||
@ -67,5 +75,7 @@ export class CustomerResponseDto {
|
||||
this.gender = customer.gender;
|
||||
this.isJunior = customer.isJunior;
|
||||
this.isGuardian = customer.isGuardian;
|
||||
this.notificationSettings = new NotificationSettingsResponseDto(customer.notificationSettings);
|
||||
this.profilePicture = customer.profilePicture ? new DocumentMetaResponseDto(customer.profilePicture) : null;
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { UserNotificationSettings } from '~/auth/entities';
|
||||
import { CustomerNotificationSettings } from '~/customer/entities/customer-notification-settings.entity';
|
||||
|
||||
export class NotificationSettingsResponseDto {
|
||||
@ApiProperty()
|
||||
@ -11,7 +11,7 @@ export class NotificationSettingsResponseDto {
|
||||
@ApiProperty()
|
||||
isSmsEnabled!: boolean;
|
||||
|
||||
constructor(notificationSettings: UserNotificationSettings) {
|
||||
constructor(notificationSettings: CustomerNotificationSettings) {
|
||||
this.isEmailEnabled = notificationSettings.isEmailEnabled;
|
||||
this.isPushEnabled = notificationSettings.isPushEnabled;
|
||||
this.isSmsEnabled = notificationSettings.isSmsEnabled;
|
||||
|
@ -8,10 +8,10 @@ import {
|
||||
PrimaryGeneratedColumn,
|
||||
UpdateDateColumn,
|
||||
} from 'typeorm';
|
||||
import { User } from './user.entity';
|
||||
import { Customer } from '~/customer/entities';
|
||||
|
||||
@Entity('user_notification_settings')
|
||||
export class UserNotificationSettings extends BaseEntity {
|
||||
@Entity('cutsomer_notification_settings')
|
||||
export class CustomerNotificationSettings extends BaseEntity {
|
||||
@PrimaryGeneratedColumn('uuid')
|
||||
id!: string;
|
||||
|
||||
@ -24,9 +24,9 @@ export class UserNotificationSettings extends BaseEntity {
|
||||
@Column({ name: 'is_sms_enabled', default: false })
|
||||
isSmsEnabled!: boolean;
|
||||
|
||||
@OneToOne(() => User, (user) => user.notificationSettings, { onDelete: 'CASCADE' })
|
||||
@JoinColumn({ name: 'user_id' })
|
||||
user!: User;
|
||||
@OneToOne(() => Customer, (customer) => customer.notificationSettings, { onDelete: 'CASCADE' })
|
||||
@JoinColumn({ name: 'customer_id' })
|
||||
customer!: Customer;
|
||||
|
||||
@CreateDateColumn({ name: 'created_at', type: 'timestamp with time zone' })
|
||||
createdAt!: Date;
|
@ -9,10 +9,12 @@ import {
|
||||
UpdateDateColumn,
|
||||
} from 'typeorm';
|
||||
import { User } from '~/auth/entities';
|
||||
import { Document } from '~/document/entities';
|
||||
import { Guardian } from '~/guardian/entities/guradian.entity';
|
||||
import { Junior } from '~/junior/entities';
|
||||
import { CustomerNotificationSettings } from './customer-notification-settings.entity';
|
||||
|
||||
@Entity()
|
||||
@Entity('customers')
|
||||
export class Customer extends BaseEntity {
|
||||
@PrimaryColumn('uuid')
|
||||
id!: string;
|
||||
@ -65,6 +67,19 @@ export class Customer extends BaseEntity {
|
||||
@Column('varchar', { name: 'user_id' })
|
||||
userId!: string;
|
||||
|
||||
@Column('varchar', { name: 'profile_picture_id', nullable: true })
|
||||
profilePictureId!: string;
|
||||
|
||||
@OneToOne(() => CustomerNotificationSettings, (notificationSettings) => notificationSettings.customer, {
|
||||
cascade: true,
|
||||
eager: true,
|
||||
})
|
||||
notificationSettings!: CustomerNotificationSettings;
|
||||
|
||||
@OneToOne(() => Document, (document) => document.customerPicture, { cascade: true, nullable: true })
|
||||
@JoinColumn({ name: 'profile_picture_id' })
|
||||
profilePicture!: Document;
|
||||
|
||||
@OneToOne(() => User, (user) => user.customer, { onDelete: 'CASCADE' })
|
||||
@JoinColumn({ name: 'user_id' })
|
||||
user!: User;
|
||||
|
@ -3,19 +3,20 @@ import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { FindOptionsWhere, Repository } from 'typeorm';
|
||||
import { User } from '~/auth/entities';
|
||||
import { Roles } from '~/auth/enums';
|
||||
import { UpdateCustomerRequestDto } from '../dtos/request';
|
||||
import { UpdateNotificationsSettingsRequestDto } from '../dtos/request';
|
||||
import { Customer } from '../entities';
|
||||
import { CustomerNotificationSettings } from '../entities/customer-notification-settings.entity';
|
||||
|
||||
@Injectable()
|
||||
export class CustomerRepository {
|
||||
constructor(@InjectRepository(Customer) private readonly customerRepository: Repository<Customer>) {}
|
||||
|
||||
updateCustomer(id: string, data: UpdateCustomerRequestDto) {
|
||||
updateCustomer(id: string, data: Partial<Customer>) {
|
||||
return this.customerRepository.update(id, data);
|
||||
}
|
||||
|
||||
findOne(where: FindOptionsWhere<Customer>) {
|
||||
return this.customerRepository.findOne({ where });
|
||||
return this.customerRepository.findOne({ where, relations: ['profilePicture'] });
|
||||
}
|
||||
|
||||
createCustomer(customerData: Partial<Customer>, user: User) {
|
||||
@ -29,4 +30,12 @@ export class CustomerRepository {
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
updateNotificationSettings(customer: Customer, body: UpdateNotificationsSettingsRequestDto) {
|
||||
customer.notificationSettings = CustomerNotificationSettings.create({
|
||||
...customer.notificationSettings,
|
||||
...body,
|
||||
});
|
||||
return this.customerRepository.save(customer);
|
||||
}
|
||||
}
|
||||
|
@ -1,18 +1,20 @@
|
||||
import { BadRequestException, forwardRef, Inject, Injectable } from '@nestjs/common';
|
||||
import { BadRequestException, Injectable } from '@nestjs/common';
|
||||
import { User } from '~/auth/entities';
|
||||
import { UserService } from '~/auth/services/user.service';
|
||||
import { OciService } from '~/document/services';
|
||||
import { UpdateCustomerRequestDto, UpdateNotificationsSettingsRequestDto } from '../dtos/request';
|
||||
import { Customer } from '../entities';
|
||||
import { CustomerRepository } from '../repositories/customer.repository';
|
||||
|
||||
@Injectable()
|
||||
export class CustomerService {
|
||||
constructor(
|
||||
@Inject(forwardRef(() => UserService)) private readonly userService: UserService,
|
||||
private readonly customerRepository: CustomerRepository,
|
||||
) {}
|
||||
updateNotificationSettings(userId: string, data: UpdateNotificationsSettingsRequestDto) {
|
||||
return this.userService.updateNotificationSettings(userId, data);
|
||||
constructor(private readonly customerRepository: CustomerRepository, private readonly ociService: OciService) {}
|
||||
async updateNotificationSettings(userId: string, data: UpdateNotificationsSettingsRequestDto) {
|
||||
const customer = await this.findCustomerById(userId);
|
||||
|
||||
const notificationSettings = (await this.customerRepository.updateNotificationSettings(customer, data))
|
||||
.notificationSettings;
|
||||
|
||||
return notificationSettings;
|
||||
}
|
||||
|
||||
async updateCustomer(userId: string, data: UpdateCustomerRequestDto): Promise<Customer> {
|
||||
@ -26,9 +28,14 @@ export class CustomerService {
|
||||
|
||||
async findCustomerById(id: string) {
|
||||
const customer = await this.customerRepository.findOne({ id });
|
||||
|
||||
if (!customer) {
|
||||
throw new BadRequestException('CUSTOMER.NOT_FOUND');
|
||||
}
|
||||
|
||||
if (customer.profilePicture) {
|
||||
customer.profilePicture.url = await this.ociService.generatePreSignedUrl(customer.profilePicture);
|
||||
}
|
||||
return customer;
|
||||
}
|
||||
}
|
||||
|
@ -9,7 +9,7 @@ export class CreateDocumentEntity1732434281561 implements MigrationInterface {
|
||||
"id" uuid NOT NULL DEFAULT uuid_generate_v4(),
|
||||
"name" character varying(255) NOT NULL,
|
||||
"extension" character varying(255) NOT NULL,
|
||||
"documentType" character varying(255) NOT NULL,
|
||||
"document_type" character varying(255) NOT NULL,
|
||||
"updated_at" TIMESTAMP NOT NULL DEFAULT now(),
|
||||
"created_at" TIMESTAMP NOT NULL DEFAULT now(),
|
||||
CONSTRAINT "PK_ac51aa5181ee2036f5ca482857c" PRIMARY KEY ("id"))`,
|
||||
|
@ -16,16 +16,10 @@ export class CreateUserEntity1733206728721 implements MigrationInterface {
|
||||
"apple_id" character varying(255),
|
||||
"is_profile_completed" boolean NOT NULL DEFAULT false,
|
||||
"roles" text array,
|
||||
"profile_picture_id" uuid,
|
||||
"created_at" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(),
|
||||
"updated_at" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(),
|
||||
CONSTRAINT "REL_02ec15de199e79a0c46869895f" UNIQUE ("profile_picture_id"),
|
||||
CONSTRAINT "PK_a3ffb1c0c8416b9fc6f907b7433" PRIMARY KEY ("id"))`,
|
||||
);
|
||||
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "users" ADD CONSTRAINT "FK_02ec15de199e79a0c46869895f4" FOREIGN KEY ("profile_picture_id") REFERENCES "documents"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`,
|
||||
);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
|
@ -1,29 +0,0 @@
|
||||
import { MigrationInterface, QueryRunner } from 'typeorm';
|
||||
|
||||
export class CreateNotificationSettingsTable1733231692252 implements MigrationInterface {
|
||||
name = 'CreateNotificationSettingsTable1733231692252';
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`CREATE TABLE "user_notification_settings"
|
||||
("id" uuid NOT NULL DEFAULT uuid_generate_v4(),
|
||||
"is_email_enabled" boolean NOT NULL DEFAULT false,
|
||||
"is_push_enabled" boolean NOT NULL DEFAULT false,
|
||||
"is_sms_enabled" boolean NOT NULL DEFAULT false,
|
||||
"created_at" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(),
|
||||
"updated_at" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(),
|
||||
"user_id" uuid, CONSTRAINT "REL_52182ffd0f785e8256f8fcb4fd" UNIQUE ("user_id"),
|
||||
CONSTRAINT "PK_a195de67d093e096152f387afbd" PRIMARY KEY ("id"))`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "user_notification_settings" ADD CONSTRAINT "FK_52182ffd0f785e8256f8fcb4fd6" FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE NO ACTION`,
|
||||
);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "user_notification_settings" DROP CONSTRAINT "FK_52182ffd0f785e8256f8fcb4fd6"`,
|
||||
);
|
||||
await queryRunner.query(`DROP TABLE "user_notification_settings"`);
|
||||
}
|
||||
}
|
@ -5,7 +5,7 @@ export class CreateCustomerEntity1733298524771 implements MigrationInterface {
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`CREATE TABLE "customer"
|
||||
`CREATE TABLE "customers"
|
||||
("id" uuid NOT NULL,
|
||||
"customer_status" character varying(255) NOT NULL DEFAULT 'PENDING',
|
||||
"rejection_reason" text,
|
||||
@ -22,19 +22,25 @@ export class CreateCustomerEntity1733298524771 implements MigrationInterface {
|
||||
"gender" character varying(255),
|
||||
"is_junior" boolean NOT NULL DEFAULT false,
|
||||
"is_guardian" boolean NOT NULL DEFAULT false,
|
||||
"profile_picture_id" uuid,
|
||||
"user_id" uuid NOT NULL,
|
||||
"createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(),
|
||||
"updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(),
|
||||
CONSTRAINT "REL_5d1f609371a285123294fddcf3" UNIQUE ("user_id"),
|
||||
CONSTRAINT "REL_5d1f609371a285123294fddcf3" UNIQUE ("user_id"),
|
||||
CONSTRAINT "REL_02ec15de199e79a0c46869895f" UNIQUE ("profile_picture_id"),
|
||||
CONSTRAINT "PK_a7a13f4cacb744524e44dfdad32" PRIMARY KEY ("id"))`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "customer" ADD CONSTRAINT "FK_5d1f609371a285123294fddcf3a" FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE NO ACTION`,
|
||||
`ALTER TABLE "customers" ADD CONSTRAINT "FK_e7574892da11dd01de5cfc46499" FOREIGN KEY ("profile_picture_id") REFERENCES "documents"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "customers" ADD CONSTRAINT "FK_11d81cd7be87b6f8865b0cf7661" FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE NO ACTION`,
|
||||
);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`ALTER TABLE "customer" DROP CONSTRAINT "FK_5d1f609371a285123294fddcf3a"`);
|
||||
await queryRunner.query(`DROP TABLE "customer"`);
|
||||
await queryRunner.query(`ALTER TABLE "customers" DROP CONSTRAINT "FK_11d81cd7be87b6f8865b0cf7661"`);
|
||||
await queryRunner.query(`ALTER TABLE "customers" DROP CONSTRAINT "FK_e7574892da11dd01de5cfc46499"`);
|
||||
await queryRunner.query(`DROP TABLE "customers"`);
|
||||
}
|
||||
}
|
||||
|
@ -24,7 +24,7 @@ export class CreateJuniorEntity1733731507261 implements MigrationInterface {
|
||||
`ALTER TABLE "juniors" ADD CONSTRAINT "FK_4662c4433223c01fe69fc1382f5" FOREIGN KEY ("civil_id_back_id") REFERENCES "documents"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "juniors" ADD CONSTRAINT "FK_dfbf64ede1ff823a489902448a2" FOREIGN KEY ("customer_id") REFERENCES "customer"("id") ON DELETE CASCADE ON UPDATE NO ACTION`,
|
||||
`ALTER TABLE "juniors" ADD CONSTRAINT "FK_dfbf64ede1ff823a489902448a2" FOREIGN KEY ("customer_id") REFERENCES "customers"("id") ON DELETE CASCADE ON UPDATE NO ACTION`,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -17,7 +17,7 @@ export class CreateGuardianEntity1733732021622 implements MigrationInterface {
|
||||
`ALTER TABLE "juniors" ADD CONSTRAINT "FK_0b11aa56264184690e2220da4a0" FOREIGN KEY ("guardian_id") REFERENCES "guardians"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "guardians" ADD CONSTRAINT "FK_6c46a1b6af00e6457cb1b70f7e7" FOREIGN KEY ("customer_id") REFERENCES "customer"("id") ON DELETE CASCADE ON UPDATE NO ACTION`,
|
||||
`ALTER TABLE "guardians" ADD CONSTRAINT "FK_6c46a1b6af00e6457cb1b70f7e7" FOREIGN KEY ("customer_id") REFERENCES "customers"("id") ON DELETE CASCADE ON UPDATE NO ACTION`,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,29 @@
|
||||
import { MigrationInterface, QueryRunner } from 'typeorm';
|
||||
|
||||
export class CreateCustomerNotificationsSettingsTable1733993920226 implements MigrationInterface {
|
||||
name = 'CreateCustomerNotificationsSettingsTable1733993920226';
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`CREATE TABLE "cutsomer_notification_settings"
|
||||
("id" uuid NOT NULL DEFAULT uuid_generate_v4(),
|
||||
"is_email_enabled" boolean NOT NULL DEFAULT false,
|
||||
"is_push_enabled" boolean NOT NULL DEFAULT false,
|
||||
"is_sms_enabled" boolean NOT NULL DEFAULT false,
|
||||
"created_at" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(),
|
||||
"updated_at" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(),
|
||||
"customer_id" uuid, CONSTRAINT "REL_32f2b707407298a9eecd6cc7ea" UNIQUE ("customer_id"),
|
||||
CONSTRAINT "PK_ea94fb22410c89ae6b37d63b0e3" PRIMARY KEY ("id"))`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "cutsomer_notification_settings" ADD CONSTRAINT "FK_32f2b707407298a9eecd6cc7ea6" FOREIGN KEY ("customer_id") REFERENCES "customers"("id") ON DELETE CASCADE ON UPDATE NO ACTION`,
|
||||
);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "cutsomer_notification_settings" DROP CONSTRAINT "FK_32f2b707407298a9eecd6cc7ea6"`,
|
||||
);
|
||||
await queryRunner.query(`DROP TABLE "cutsomer_notification_settings"`);
|
||||
}
|
||||
}
|
@ -1,7 +1,6 @@
|
||||
export * from './1732434281561-create-document-entity';
|
||||
export * from './1733206728721-create-user-entity';
|
||||
export * from './1733209041336-create-otp-entity';
|
||||
export * from './1733231692252-create-notification-settings-table';
|
||||
export * from './1733298524771-create-customer-entity';
|
||||
export * from './1733314952318-create-device-entity';
|
||||
export * from './1733731507261-create-junior-entity';
|
||||
@ -10,3 +9,4 @@ export * from './1733748083604-create-theme-entity';
|
||||
export * from './1733750228289-seed-default-avatar';
|
||||
export * from './1733904556416-create-task-entities';
|
||||
export * from './1733990253208-seeds-default-tasks-logo';
|
||||
export * from './1733993920226-create-customer-notifications-settings-table';
|
||||
|
@ -18,19 +18,11 @@ export class DocumentMetaResponseDto {
|
||||
@ApiProperty({ type: String })
|
||||
url!: string | null;
|
||||
|
||||
@ApiProperty()
|
||||
createdAt!: Date;
|
||||
|
||||
@ApiProperty()
|
||||
updatedAt!: Date;
|
||||
|
||||
constructor(document: Document) {
|
||||
this.id = document.id;
|
||||
this.name = document.name;
|
||||
this.extension = document.extension;
|
||||
this.documentType = document.documentType;
|
||||
this.url = document.url || null;
|
||||
this.createdAt = document.createdAt;
|
||||
this.updatedAt = document.updatedAt;
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { Column, Entity, OneToMany, OneToOne, PrimaryGeneratedColumn, UpdateDateColumn } from 'typeorm';
|
||||
import { User } from '~/auth/entities';
|
||||
import { Customer } from '~/customer/entities';
|
||||
import { Junior, Theme } from '~/junior/entities';
|
||||
import { Task } from '~/task/entities';
|
||||
import { TaskSubmission } from '~/task/entities/task-submissions.entity';
|
||||
@ -16,16 +17,16 @@ export class Document {
|
||||
@Column({ type: 'varchar', length: 255 })
|
||||
extension!: string;
|
||||
|
||||
@Column({ type: 'varchar', length: 255 })
|
||||
@Column({ type: 'varchar', length: 255, name: 'document_type' })
|
||||
documentType!: DocumentType;
|
||||
|
||||
@OneToOne(() => User, (user) => user.profilePicture, { onDelete: 'CASCADE' })
|
||||
user?: User;
|
||||
@OneToOne(() => Customer, (customer) => customer.profilePicture, { onDelete: 'SET NULL' })
|
||||
customerPicture?: Customer;
|
||||
|
||||
@OneToOne(() => Junior, (junior) => junior.civilIdFront, { onDelete: 'CASCADE' })
|
||||
@OneToOne(() => Junior, (junior) => junior.civilIdFront, { onDelete: 'SET NULL' })
|
||||
juniorCivilIdFront?: User;
|
||||
|
||||
@OneToOne(() => Junior, (junior) => junior.civilIdBack, { onDelete: 'CASCADE' })
|
||||
@OneToOne(() => Junior, (junior) => junior.civilIdBack, { onDelete: 'SET NULL' })
|
||||
juniorCivilIdBack?: User;
|
||||
|
||||
@OneToMany(() => Theme, (theme) => theme.avatar)
|
||||
|
@ -70,6 +70,7 @@ export class OciService {
|
||||
return null;
|
||||
}
|
||||
const cachedUrl = await this.cacheService.get<string>(document.id);
|
||||
|
||||
if (cachedUrl) {
|
||||
return cachedUrl;
|
||||
}
|
||||
@ -82,7 +83,7 @@ export class OciService {
|
||||
this.logger.debug(`Generating pre-signed url for object ${objectName} in bucket ${bucketName}`);
|
||||
const res = await this.ociClient.createPreauthenticatedRequest({
|
||||
namespaceName: this.namespace,
|
||||
bucketName,
|
||||
bucketName: 'asd',
|
||||
createPreauthenticatedRequestDetails: {
|
||||
name: objectName,
|
||||
accessType: CreatePreauthenticatedRequestDetails.AccessType.AnyObjectRead,
|
||||
@ -95,7 +96,7 @@ export class OciService {
|
||||
this.cacheService.set(document.id, res.preauthenticatedRequest.fullPath + objectName, '1h');
|
||||
return res.preauthenticatedRequest.fullPath + objectName;
|
||||
} catch (error) {
|
||||
this.logger.error('Error generating pre-signed url', JSON.stringify(error));
|
||||
this.logger.error(`Error generating pre-signed url: ${error}`);
|
||||
return document.name;
|
||||
}
|
||||
}
|
||||
|
@ -20,8 +20,8 @@ export class JuniorResponseDto {
|
||||
this.id = junior.id;
|
||||
this.fullName = `${junior.customer.firstName} ${junior.customer.lastName}`;
|
||||
this.relationship = junior.relationship;
|
||||
this.profilePicture = junior.customer.user.profilePicture
|
||||
? new DocumentMetaResponseDto(junior.customer.user.profilePicture)
|
||||
this.profilePicture = junior.customer.profilePicture
|
||||
? new DocumentMetaResponseDto(junior.customer.profilePicture)
|
||||
: null;
|
||||
}
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ import { Transactional } from 'typeorm-transactional';
|
||||
import { Roles } from '~/auth/enums';
|
||||
import { UserService } from '~/auth/services';
|
||||
import { PageOptionsRequestDto } from '~/core/dtos';
|
||||
import { CustomerNotificationSettings } from '~/customer/entities/customer-notification-settings.entity';
|
||||
import { CustomerService } from '~/customer/services';
|
||||
import { CreateJuniorRequestDto, SetThemeRequestDto } from '../dtos/request';
|
||||
import { Junior } from '../entities';
|
||||
@ -43,6 +44,7 @@ export class JuniorService {
|
||||
civilIdFrontId: body.civilIdFrontId,
|
||||
civilIdBackId: body.civilIdBackId,
|
||||
}),
|
||||
notificationSettings: new CustomerNotificationSettings(),
|
||||
},
|
||||
user,
|
||||
);
|
||||
|
@ -80,7 +80,7 @@ export class TaskService {
|
||||
const [imageUrl, submissionUrl, profilePictureUrl] = await Promise.all([
|
||||
this.ociService.generatePreSignedUrl(task.image),
|
||||
this.ociService.generatePreSignedUrl(task.submission?.proofOfCompletion),
|
||||
this.ociService.generatePreSignedUrl(task.assignedTo.customer.user.profilePicture),
|
||||
this.ociService.generatePreSignedUrl(task.assignedTo.customer.profilePicture),
|
||||
]);
|
||||
|
||||
task.image.url = imageUrl;
|
||||
@ -89,8 +89,8 @@ export class TaskService {
|
||||
task.submission.proofOfCompletion.url = submissionUrl;
|
||||
}
|
||||
|
||||
if (task.assignedTo.customer.user.profilePicture) {
|
||||
task.assignedTo.customer.user.profilePicture.url = profilePictureUrl;
|
||||
if (task.assignedTo.customer.profilePicture) {
|
||||
task.assignedTo.customer.profilePicture.url = profilePictureUrl;
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
Reference in New Issue
Block a user