Skip to content

Commit

Permalink
WIP: server select to load infinite data (#322)
Browse files Browse the repository at this point in the history
* wip: inifinite loading select

* fix: select - add limit 10 for VolunteerSelect and remove the hardcoded value in listing/volunteer ctrl

* fix: select - allow listing/events to limit, set limit to 10 in EventSelect

* fix: select - add page and limit to listing/admins, set limit to 10

* fix: select - add pagination to location/city

---------

Co-authored-by: luciatugui <[email protected]>
  • Loading branch information
radulescuandrew and luciatugui authored Jul 22, 2024
1 parent 71feee8 commit 906881b
Show file tree
Hide file tree
Showing 24 changed files with 386 additions and 106 deletions.
5 changes: 2 additions & 3 deletions backend/src/api/activity-type/activity-type.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,8 @@ export class ActivityTypeController {
async get(
@Param('id', UuidValidationPipe) activityTypeId: string,
): Promise<ActivityTypePresenter> {
const accessRequest = await this.getOneActivityTypeUseCase.execute(
activityTypeId,
);
const accessRequest =
await this.getOneActivityTypeUseCase.execute(activityTypeId);
return new ActivityTypePresenter(accessRequest);
}

Expand Down
3 changes: 0 additions & 3 deletions backend/src/api/listing/listing.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@ export class ListingController {
): Promise<PaginatedPresenter<IdAndNamePresenter<IEventModel>>> {
const events = await this.getManyEventUseCase.execute({
...filters,
limit: 50,
organizationId,
status: EventStatus.PUBLISHED,
});
Expand All @@ -67,7 +66,6 @@ export class ListingController {
const volunteers = await this.getManyVolunteersUseCase.execute({
...filters,
organizationId: user.organizationId,
limit: 50,
});

return new PaginatedPresenter({
Expand Down Expand Up @@ -103,7 +101,6 @@ export class ListingController {
): Promise<PaginatedPresenter<IdAndNamePresenter<IAdminUserModel>>> {
const admins = await this.getManyAdminUserUseCase.execute({
...filters,
limit: 50,
organizationId,
});

Expand Down
16 changes: 6 additions & 10 deletions backend/src/api/location/dto/get-city.dto.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,12 @@
import { IsOptional, IsString, MinLength } from 'class-validator';
import { IsOptional, IsPositive, IsString } from 'class-validator';
import { BasePaginationFilterDto } from 'src/infrastructure/base/base-pagination-filter.dto';

export class GetCityDto {
export class GetCityDto extends BasePaginationFilterDto {
@IsString()
@IsOptional()
@MinLength(3)
search: string;
search?: string;

@IsString()
@IsOptional()
city: string;

@IsString()
@IsPositive()
@IsOptional()
county: string;
countyId?: number;
}
17 changes: 9 additions & 8 deletions backend/src/api/location/location.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { CountyPresenter } from './presenters/county.presenter';
import { Public } from 'src/common/decorators/public.decorator';
import { ApiParam } from '@nestjs/swagger';
import { GetCitiesByCountyIdUseCase } from 'src/usecases/location/get-cities-by-county-id.usecase';
import { PaginatedPresenter } from 'src/infrastructure/presenters/generic-paginated.presenter';

@Public()
@Controller('location')
Expand All @@ -19,14 +20,14 @@ export class LocationController {

@Get('city')
async getCities(
@Query() { search, city, county }: GetCityDto,
): Promise<CityPresenter[]> {
const cities = await this.getCitiesUseCase.execute({
search,
city,
county,
});
return cities.map((city) => new CityPresenter(city));
@Query() options: GetCityDto,
): Promise<PaginatedPresenter<CityPresenter>> {
const cities = await this.getCitiesUseCase.execute(options);

return {
items: cities.items.map((city) => new CityPresenter(city)),
meta: cities.meta,
};
}

@ApiParam({ name: 'countyId', type: 'number' })
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import { BaseEntity } from 'src/infrastructure/base/base-entity';
import { Pagination } from 'src/infrastructure/base/repository-with-pagination.class';
import { SelectQueryBuilder } from 'typeorm';

export interface IRepositoryWithPagination<T extends BaseEntity> {
export interface IRepositoryWithPagination<T> {
paginateQuery<TModel>(
query: SelectQueryBuilder<T>,
limit: number,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import { Brackets, Repository, SelectQueryBuilder } from 'typeorm';
import { format } from 'date-fns';
import { DATE_CONSTANTS } from 'src/common/constants/constants';
import { IRepositoryWithPagination } from 'src/common/interfaces/repository-with-pagination.interface';
import { BaseEntity } from './base-entity';

export interface IPaginationMeta {
itemCount: number;
Expand All @@ -17,7 +16,7 @@ export interface Pagination<T> {
meta: IPaginationMeta;
}

export abstract class RepositoryWithPagination<T extends BaseEntity>
export abstract class RepositoryWithPagination<T>
implements IRepositoryWithPagination<T>
{
constructor(private readonly repository: Repository<T>) {}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { Pagination } from 'src/infrastructure/base/repository-with-pagination.class';
import { FindLocationOptions, ICityModel } from '../model/city.model';
import { ICountyModel } from '../model/county.model';

export interface ILocationRepository {
findCounties(): Promise<ICountyModel[]>;
findCities(options: FindLocationOptions): Promise<ICityModel[]>;
findCities(options: FindLocationOptions): Promise<Pagination<ICityModel>>;
findCitiesByCountyId(countyId: number): Promise<ICityModel[]>;
}
6 changes: 3 additions & 3 deletions backend/src/modules/location/model/city.model.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { IBasePaginationFilterModel } from 'src/infrastructure/base/base-pagination-filter.model';
import { CityEntity } from '../entities/city.entity';
import { ICountyModel } from './county.model';

Expand All @@ -9,9 +10,8 @@ export interface ICityModel {

export type FindLocationOptions = {
search?: string;
city?: string;
county?: string;
};
countyId?: number;
} & IBasePaginationFilterModel;

export class CityTransformer {
static fromEntity(entity: CityEntity): ICityModel {
Expand Down
59 changes: 41 additions & 18 deletions backend/src/modules/location/repositories/location.repository.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { ILike, Repository } from 'typeorm';
import { Repository } from 'typeorm';
import { CityEntity } from '../entities/city.entity';
import { CountyEntity } from '../entities/county.entity';
import { ILocationRepository } from '../interfaces/location-repository.interface';
Expand All @@ -10,36 +10,59 @@ import {
ICityModel,
} from '../model/city.model';
import { CountyTransformer, ICountyModel } from '../model/county.model';
import {
Pagination,
RepositoryWithPagination,
} from 'src/infrastructure/base/repository-with-pagination.class';
import { OrderDirection } from 'src/common/enums/order-direction.enum';

@Injectable()
export class LocationRepositoryService implements ILocationRepository {
export class LocationRepositoryService
extends RepositoryWithPagination<CityEntity>
implements ILocationRepository
{
constructor(
@InjectRepository(CityEntity)
private readonly cityRepository: Repository<CityEntity>,
@InjectRepository(CountyEntity)
private readonly countyRepository: Repository<CountyEntity>,
) {}
) {
super(cityRepository);
}

async findCounties(): Promise<ICountyModel[]> {
const countyEntities = await this.countyRepository.find();
return countyEntities.map(CountyTransformer.fromEntity);
}

// This will only find the cities which start with the search word
async findCities(options: FindLocationOptions): Promise<ICityModel[]> {
const { search, city, county } = options;
const cityEntities = await this.cityRepository.find({
where: {
name: ILike(`${search}%`),
...(city && county
? { county: { abbreviation: county }, name: city }
: {}),
},
relations: {
county: true,
},
});
return cityEntities.map(CityTransformer.fromEntity);
async findCities(
options: FindLocationOptions,
): Promise<Pagination<ICityModel>> {
const { search, countyId } = options;

const query = this.cityRepository
.createQueryBuilder('city')
.leftJoinAndMapOne('city.county', 'city.county', 'county')
.select()
.orderBy(
this.buildOrderByQuery(options.orderBy || 'name', 'city'),
options.orderDirection || OrderDirection.ASC,
);

if (search) {
query.andWhere(this.buildBracketSearchQuery(['city.name'], search));
}

if (countyId) {
query.andWhere('city.countyId = :countyId', { countyId });
}

return this.paginateQuery(
query,
options.limit,
options.page,
CityTransformer.fromEntity,
);
}

async findCitiesByCountyId(countyId: number): Promise<ICityModel[]> {
Expand Down
5 changes: 4 additions & 1 deletion backend/src/modules/location/services/location.facade.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,15 @@ import { Injectable } from '@nestjs/common';
import { FindLocationOptions, ICityModel } from '../model/city.model';
import { ICountyModel } from '../model/county.model';
import { LocationRepositoryService } from '../repositories/location.repository';
import { Pagination } from 'src/infrastructure/base/repository-with-pagination.class';

@Injectable()
export class LocationFacade {
constructor(private readonly locationRepository: LocationRepositoryService) {}

public async findCities(options: FindLocationOptions): Promise<ICityModel[]> {
public async findCities(
options: FindLocationOptions,
): Promise<Pagination<ICityModel>> {
return this.locationRepository.findCities(options);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -331,9 +331,8 @@ export class VolunteerRepositoryService
);
}

const deletedVolunteerRecords = await this.volunteerRepository.softRemove(
volunteerRecords,
);
const deletedVolunteerRecords =
await this.volunteerRepository.softRemove(volunteerRecords);

return {
deletedProfiles: deletedProfiles?.map((dp) => dp.id) || [],
Expand Down
9 changes: 7 additions & 2 deletions backend/src/usecases/location/get-citties.usecase.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,21 @@
import { Injectable } from '@nestjs/common';
import { IUseCaseService } from 'src/common/interfaces/use-case-service.interface';
import { Pagination } from 'src/infrastructure/base/repository-with-pagination.class';
import {
FindLocationOptions,
ICityModel,
} from 'src/modules/location/model/city.model';
import { LocationFacade } from 'src/modules/location/services/location.facade';

@Injectable()
export class GetCitiesUseCase implements IUseCaseService<ICityModel[]> {
export class GetCitiesUseCase
implements IUseCaseService<Pagination<ICityModel>>
{
constructor(private readonly locationFacade: LocationFacade) {}

public async execute(options: FindLocationOptions): Promise<ICityModel[]> {
public async execute(
options: FindLocationOptions,
): Promise<Pagination<ICityModel>> {
return this.locationFacade.findCities(options);
}
}
Loading

0 comments on commit 906881b

Please sign in to comment.