fix(change-position) perbaikan change position module

pull/32/head
Aswin Ashar Abdullah 2024-07-18 13:36:22 +07:00
parent 12359f3685
commit 37b12c960f
23 changed files with 328 additions and 0 deletions

View File

@ -21,6 +21,15 @@ export abstract class BaseDataService<Entity> {
return await queryRunner.manager.save(newEntity);
}
async createMany(
queryRunner: QueryRunner,
entityTarget: EntityTarget<Entity>,
entity: Entity[],
): Promise<Entity[]> {
const newEntity = queryRunner.manager.create(entityTarget, entity);
return await queryRunner.manager.save(newEntity);
}
async createBatch(
queryRunner: QueryRunner,
entityTarget: EntityTarget<Entity>,

View File

@ -0,0 +1,152 @@
import { BaseManager } from '../base.manager';
import {
EventTopics,
columnUniques,
validateRelations,
} from 'src/core/strings/constants/interface.constants';
import { HttpStatus, UnprocessableEntityException } from '@nestjs/common';
import { SelectQueryBuilder } from 'typeorm';
export abstract class BaseChangePosition<Entity> extends BaseManager {
protected result: Entity;
protected duplicateColumn: string[];
protected startData: Entity;
protected endData: Entity;
protected columnSort: string;
protected firstDataId: number;
protected lastSort: number;
protected sortTo: number;
abstract get entityTarget(): any;
setData(entity: Entity, columnSort: string): void {
this.data = entity;
this.columnSort = columnSort;
}
async beforeProcess(): Promise<void> {
if (!this.data?.end || this.data.start == this.data?.end) {
throw new UnprocessableEntityException({
statusCode: HttpStatus.UNPROCESSABLE_ENTITY,
message: 'Please drag to another position',
error: 'Unprocessable Entity',
});
}
this.startData = await this.dataService.getOneByOptions({
where: {
id: this.data.start,
},
});
if (!this.startData) {
throw new UnprocessableEntityException({
statusCode: HttpStatus.UNPROCESSABLE_ENTITY,
message: `Entity with id : ${this.data.start} not found`,
error: 'Unprocessable Entity',
});
}
this.endData = await this.dataService.getOneByOptions({
where: {
id: this.data.end,
},
});
if (!this.endData) {
throw new UnprocessableEntityException({
statusCode: HttpStatus.UNPROCESSABLE_ENTITY,
message: `Entity with id : ${this.data.end} not found`,
error: 'Unprocessable Entity',
});
}
if (this.endData[this.columnSort] > this.startData[this.columnSort]) {
// drag from up
this.firstDataId = this.startData[this.columnSort];
this.lastSort = this.endData[this.columnSort];
this.sortTo = this.lastSort;
} else if (
this.endData[this.columnSort] < this.startData[this.columnSort]
) {
// drag from bottom
this.firstDataId = this.endData[this.columnSort];
this.lastSort = this.startData[this.columnSort];
this.sortTo = this.firstDataId;
}
}
async prepareData(): Promise<void> {
Object.assign(this.data, {
creator_id: this.user.id,
creator_name: this.user.name,
created_at: new Date().getTime(),
updated_at: new Date().getTime(),
});
}
async validateProcess(): Promise<void> {
return;
}
async process(): Promise<void> {
let dataArrange: Entity[];
const queryBuilder = this.dataService
.getRepository()
.createQueryBuilder(this.tableName)
.where(`${this.tableName}.${this.columnSort} between :data1 and :data2`, {
data1: this.firstDataId,
data2: this.lastSort,
});
const datas = await queryBuilder
.orderBy(`${this.tableName}.${this.columnSort}`, 'ASC')
.getManyAndCount();
if (datas[0].length) {
let dataFirst = datas[0][0][this.columnSort];
const data = datas[0];
const length = datas[1];
if (this.endData[this.columnSort] > this.startData[this.columnSort]) {
// drag from above
const dataDragged = data[0];
const arraySlice = data.slice(1, length);
dataArrange = arraySlice.concat([dataDragged]);
} else if (
this.endData[this.columnSort] < this.startData[this.columnSort]
) {
// drag from bottom
const dataDragged = data[length - 1];
const arraySlice = data.slice(0, length - 1);
dataArrange = [dataDragged].concat(arraySlice);
}
for (let i = 0; i < length; i++) {
dataArrange[i][this.columnSort] = dataFirst;
dataFirst++;
}
await this.dataService.createMany(
this.queryRunner,
this.entityTarget,
dataArrange,
);
}
}
get validateRelations(): validateRelations[] {
return [];
}
get eventTopics(): EventTopics[] {
return [];
}
getResult(): string {
return `Success! Data ${this.startData['name']} successfully moved to ${this.sortTo}`;
}
}

View File

@ -0,0 +1,4 @@
export class ChangePositionDto {
start: string;
end: string;
}

View File

@ -0,0 +1,21 @@
import { MigrationInterface, QueryRunner } from 'typeorm';
export class UpdateSortColumn1721284172572 implements MigrationInterface {
name = 'UpdateSortColumn1721284172572';
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`ALTER TABLE "term_conditions" ADD "sort_order" integer NOT NULL DEFAULT '0'`,
);
await queryRunner.query(
`ALTER TABLE "faqs" ADD "sort_order" integer NOT NULL DEFAULT '0'`,
);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE "faqs" DROP COLUMN "sort_order"`);
await queryRunner.query(
`ALTER TABLE "term_conditions" DROP COLUMN "sort_order"`,
);
}
}

View File

@ -0,0 +1,25 @@
import { MigrationInterface, QueryRunner } from 'typeorm';
export class UpdateMidtransColumnTransaction1721284234428
implements MigrationInterface
{
name = 'UpdateMidtransColumnTransaction1721284234428';
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`ALTER TABLE "transactions" DROP COLUMN "payment_midtrans_url"`,
);
await queryRunner.query(
`ALTER TABLE "transactions" DROP COLUMN "payment_midtrans_token"`,
);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`ALTER TABLE "transactions" ADD "payment_midtrans_token" character varying`,
);
await queryRunner.query(
`ALTER TABLE "transactions" ADD "payment_midtrans_url" character varying`,
);
}
}

View File

@ -5,6 +5,9 @@ import { BaseStatusModel } from 'src/core/modules/data/model/base-status.model';
@Entity(TABLE_NAME.FAQ)
export class FaqModel extends BaseStatusModel<FaqEntity> implements FaqEntity {
@Column('int', { name: 'sort_order', default: 0 })
sort_order: number;
@Column('varchar', { name: 'title', nullable: true })
title: string;

View File

@ -14,4 +14,16 @@ export class FaqDataService extends BaseDataService<FaqEntity> {
) {
super(repo);
}
async getSortColumn(): Promise<number> {
const query = this.repo.createQueryBuilder('data');
const sortColumn = await query
.select('data.sort_order')
.orderBy('data.sort_order', 'DESC')
.getOne();
const lastColumn = sortColumn?.sort_order ?? 0;
return lastColumn + 1;
}
}

View File

@ -1,6 +1,7 @@
import { BaseStatusEntity } from 'src/core/modules/domain/entities/base-status.entity';
export interface FaqEntity extends BaseStatusEntity {
sort_order: number;
title: string;
description: string;
}

View File

@ -15,6 +15,7 @@ import { BatchInactiveFaqManager } from './managers/batch-inactive-faq.manager';
import { BatchActiveFaqManager } from './managers/batch-active-faq.manager';
import { BatchDeleteFaqManager } from './managers/batch-delete-faq.manager';
import { TABLE_NAME } from 'src/core/strings/constants/table.constants';
import { ChangePositionFaqManager } from './managers/change-position-faq.manager';
@Injectable()
export class FaqDataOrchestrator extends BaseDataTransactionOrchestrator<FaqEntity> {
@ -29,11 +30,19 @@ export class FaqDataOrchestrator extends BaseDataTransactionOrchestrator<FaqEnti
private batchActiveManager: BatchActiveFaqManager,
private batchConfirmManager: BatchConfirmFaqManager,
private batchInactiveManager: BatchInactiveFaqManager,
private changePositionManager: ChangePositionFaqManager,
private serviceData: FaqDataService,
) {
super();
}
async changePostion(data): Promise<string> {
this.changePositionManager.setData(data, 'sort_order');
this.changePositionManager.setService(this.serviceData, TABLE_NAME.FAQ);
await this.changePositionManager.execute();
return this.changePositionManager.getResult();
}
async create(data): Promise<FaqEntity> {
this.createManager.setData(data);
this.createManager.setService(this.serviceData, TABLE_NAME.FAQ);

View File

@ -0,0 +1,15 @@
import { Injectable } from '@nestjs/common';
import { BaseChangePosition } from 'src/core/modules/domain/usecase/managers/base-change-position.manager';
import { FaqEntity } from '../../entities/faq.entity';
import { FaqModel } from '../../../data/models/faq.model';
@Injectable()
export class ChangePositionFaqManager extends BaseChangePosition<FaqEntity> {
get entityTarget(): any {
return FaqModel;
}
async afterProcess(): Promise<void> {
return;
}
}

View File

@ -12,6 +12,11 @@ import { FaqCreatedEvent } from '../../entities/event/faq-created.event';
@Injectable()
export class CreateFaqManager extends BaseCreateManager<FaqEntity> {
async beforeProcess(): Promise<void> {
const sortColumn = await this.dataService.getSortColumn();
Object.assign(this.data, {
sort_order: sortColumn,
});
return;
}

View File

@ -22,6 +22,7 @@ import { BatchActiveFaqManager } from './domain/usecases/managers/batch-active-f
import { BatchConfirmFaqManager } from './domain/usecases/managers/batch-confirm-faq.manager';
import { BatchInactiveFaqManager } from './domain/usecases/managers/batch-inactive-faq.manager';
import { FaqModel } from './data/models/faq.model';
import { ChangePositionFaqManager } from './domain/usecases/managers/change-position-faq.manager';
@Module({
imports: [
@ -43,6 +44,7 @@ import { FaqModel } from './data/models/faq.model';
BatchActiveFaqManager,
BatchConfirmFaqManager,
BatchInactiveFaqManager,
ChangePositionFaqManager,
FaqDataService,
FaqReadService,

View File

@ -2,8 +2,12 @@ import { BaseStatusDto } from 'src/core/modules/infrastructure/dto/base-status.d
import { FaqEntity } from '../../domain/entities/faq.entity';
import { IsString } from 'class-validator';
import { ApiProperty } from '@nestjs/swagger';
import { Exclude } from 'class-transformer';
export class FaqDto extends BaseStatusDto implements FaqEntity {
@Exclude()
sort_order: number;
@ApiProperty({
type: String,
required: true,

View File

@ -15,6 +15,7 @@ import { FaqEntity } from '../domain/entities/faq.entity';
import { BatchResult } from 'src/core/response/domain/ok-response.interface';
import { BatchIdsDto } from 'src/core/modules/infrastructure/dto/base-batch.dto';
import { Public } from 'src/core/guards';
import { ChangePositionDto } from 'src/core/modules/infrastructure/dto/base-change-position.dto';
@ApiTags(`${MODULE_NAME.FAQ.split('-').join(' ')} - data`)
@Controller(`v1/${MODULE_NAME.FAQ}`)
@ -28,6 +29,11 @@ export class FaqDataController {
return await this.orchestrator.create(data);
}
@Post('/change-position')
async dragDrop(@Body() body: ChangePositionDto): Promise<string> {
return await this.orchestrator.changePostion(body);
}
@Put('/batch-delete')
async batchDeleted(@Body() body: BatchIdsDto): Promise<BatchResult> {
return await this.orchestrator.batchDelete(body.ids);

View File

@ -8,6 +8,9 @@ export class TermConditionModel
extends BaseStatusModel<TermConditionEntity>
implements TermConditionEntity
{
@Column('int', { name: 'sort_order', default: 0 })
sort_order: number;
@Column('varchar', { name: 'title', nullable: true })
title: string;

View File

@ -14,4 +14,16 @@ export class TermConditionDataService extends BaseDataService<TermConditionEntit
) {
super(repo);
}
async getSortColumn(): Promise<number> {
const query = this.repo.createQueryBuilder('data');
const sortColumn = await query
.select('data.sort_order')
.orderBy('data.sort_order', 'DESC')
.getOne();
const lastColumn = sortColumn?.sort_order ?? 0;
return lastColumn + 1;
}
}

View File

@ -1,6 +1,7 @@
import { BaseStatusEntity } from 'src/core/modules/domain/entities/base-status.entity';
export interface TermConditionEntity extends BaseStatusEntity {
sort_order: number;
title: string;
description: string;
}

View File

@ -0,0 +1,15 @@
import { Injectable } from '@nestjs/common';
import { BaseChangePosition } from 'src/core/modules/domain/usecase/managers/base-change-position.manager';
import { TermConditionEntity } from '../../entities/term-condition.entity';
import { TermConditionModel } from '../../../data/models/term-condition.model';
@Injectable()
export class ChangePositionTermConditionManager extends BaseChangePosition<TermConditionEntity> {
get entityTarget(): any {
return TermConditionModel;
}
async afterProcess(): Promise<void> {
return;
}
}

View File

@ -12,6 +12,11 @@ import { TermConditionCreatedEvent } from '../../entities/event/term-condition-c
@Injectable()
export class CreateTermConditionManager extends BaseCreateManager<TermConditionEntity> {
async beforeProcess(): Promise<void> {
const sortColumn = await this.dataService.getSortColumn();
Object.assign(this.data, {
sort_order: sortColumn,
});
return;
}

View File

@ -15,6 +15,7 @@ import { BatchInactiveTermConditionManager } from './managers/batch-inactive-ter
import { BatchActiveTermConditionManager } from './managers/batch-active-term-condition.manager';
import { BatchDeleteTermConditionManager } from './managers/batch-delete-term-condition.manager';
import { TABLE_NAME } from 'src/core/strings/constants/table.constants';
import { ChangePositionTermConditionManager } from './managers/change-position-term-condition.manager';
@Injectable()
export class TermConditionDataOrchestrator extends BaseDataTransactionOrchestrator<TermConditionEntity> {
@ -29,11 +30,22 @@ export class TermConditionDataOrchestrator extends BaseDataTransactionOrchestrat
private batchActiveManager: BatchActiveTermConditionManager,
private batchConfirmManager: BatchConfirmTermConditionManager,
private batchInactiveManager: BatchInactiveTermConditionManager,
private changePositionManager: ChangePositionTermConditionManager,
private serviceData: TermConditionDataService,
) {
super();
}
async changePostion(data): Promise<string> {
this.changePositionManager.setData(data, 'sort_order');
this.changePositionManager.setService(
this.serviceData,
TABLE_NAME.TERM_CONDITION,
);
await this.changePositionManager.execute();
return this.changePositionManager.getResult();
}
async create(data): Promise<TermConditionEntity> {
this.createManager.setData(data);
this.createManager.setService(this.serviceData, TABLE_NAME.TERM_CONDITION);

View File

@ -2,11 +2,15 @@ import { BaseStatusDto } from 'src/core/modules/infrastructure/dto/base-status.d
import { TermConditionEntity } from '../../domain/entities/term-condition.entity';
import { ApiProperty } from '@nestjs/swagger';
import { IsString } from 'class-validator';
import { Exclude } from 'class-transformer';
export class TermConditionDto
extends BaseStatusDto
implements TermConditionEntity
{
@Exclude()
sort_order: number;
@ApiProperty({
type: String,
required: true,

View File

@ -15,6 +15,7 @@ import { TermConditionEntity } from '../domain/entities/term-condition.entity';
import { BatchResult } from 'src/core/response/domain/ok-response.interface';
import { BatchIdsDto } from 'src/core/modules/infrastructure/dto/base-batch.dto';
import { Public } from 'src/core/guards';
import { ChangePositionDto } from 'src/core/modules/infrastructure/dto/base-change-position.dto';
@ApiTags(`${MODULE_NAME.TERM_CONDITION.split('-').join(' ')} - data`)
@Controller(`v1/${MODULE_NAME.TERM_CONDITION}`)
@ -28,6 +29,11 @@ export class TermConditionDataController {
return await this.orchestrator.create(data);
}
@Post('/change-position')
async dragDrop(@Body() body: ChangePositionDto): Promise<string> {
return await this.orchestrator.changePostion(body);
}
@Put('/batch-delete')
async batchDeleted(@Body() body: BatchIdsDto): Promise<BatchResult> {
return await this.orchestrator.batchDelete(body.ids);

View File

@ -22,6 +22,7 @@ import { BatchActiveTermConditionManager } from './domain/usecases/managers/batc
import { BatchConfirmTermConditionManager } from './domain/usecases/managers/batch-confirm-term-condition.manager';
import { BatchInactiveTermConditionManager } from './domain/usecases/managers/batch-inactive-term-condition.manager';
import { TermConditionModel } from './data/models/term-condition.model';
import { ChangePositionTermConditionManager } from './domain/usecases/managers/change-position-term-condition.manager';
@Module({
imports: [
@ -43,6 +44,7 @@ import { TermConditionModel } from './data/models/term-condition.model';
BatchActiveTermConditionManager,
BatchConfirmTermConditionManager,
BatchInactiveTermConditionManager,
ChangePositionTermConditionManager,
TermConditionDataService,
TermConditionReadService,