feat: Add public endpoint and service to retrieve and merge filterable profile fields with options.
This commit is contained in:
@@ -16,6 +16,7 @@ import { CreateFieldDto, UpdateFieldDto } from './dto';
|
|||||||
import { JwtAuthGuard } from '../auth/guards/jwt-auth.guard';
|
import { JwtAuthGuard } from '../auth/guards/jwt-auth.guard';
|
||||||
import { RolesGuard } from '../auth/guards/roles.guard';
|
import { RolesGuard } from '../auth/guards/roles.guard';
|
||||||
import { Roles } from '../auth/decorators/roles.decorator';
|
import { Roles } from '../auth/decorators/roles.decorator';
|
||||||
|
import { Public } from '../auth/decorators/public.decorator';
|
||||||
import { UserRole } from '@prisma/client';
|
import { UserRole } from '@prisma/client';
|
||||||
|
|
||||||
@ApiTags('Profile Fields')
|
@ApiTags('Profile Fields')
|
||||||
@@ -23,6 +24,13 @@ import { UserRole } from '@prisma/client';
|
|||||||
export class ProfileFieldsController {
|
export class ProfileFieldsController {
|
||||||
constructor(private readonly fieldsService: ProfileFieldsService) {}
|
constructor(private readonly fieldsService: ProfileFieldsService) {}
|
||||||
|
|
||||||
|
@Public()
|
||||||
|
@Get('filterable')
|
||||||
|
@ApiOperation({ summary: 'Get all filterable fields with options (Public)' })
|
||||||
|
getFilterableFields() {
|
||||||
|
return this.fieldsService.getFilterableFields();
|
||||||
|
}
|
||||||
|
|
||||||
@Post()
|
@Post()
|
||||||
@UseGuards(JwtAuthGuard, RolesGuard)
|
@UseGuards(JwtAuthGuard, RolesGuard)
|
||||||
@Roles(UserRole.ADMIN)
|
@Roles(UserRole.ADMIN)
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Injectable, NotFoundException, ConflictException, BadRequestException } from '@nestjs/common';
|
import { Injectable, NotFoundException, ConflictException, BadRequestException } from '@nestjs/common';
|
||||||
import { Prisma } from '@prisma/client';
|
import { Prisma, FieldType } from '@prisma/client';
|
||||||
import { PrismaService } from '../prisma';
|
import { PrismaService } from '../prisma';
|
||||||
import { CreateFieldDto, UpdateFieldDto } from './dto';
|
import { CreateFieldDto, UpdateFieldDto } from './dto';
|
||||||
|
|
||||||
@@ -230,4 +230,70 @@ export class ProfileFieldsService {
|
|||||||
|
|
||||||
return { success: true, updated: updates.length };
|
return { success: true, updated: updates.length };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all filterable fields (public endpoint for search filters)
|
||||||
|
* Returns fields with CHECKBOX_GROUP, SELECT, MULTI_SELECT types that have options
|
||||||
|
*/
|
||||||
|
async getFilterableFields() {
|
||||||
|
const filterableTypes: FieldType[] = [FieldType.CHECKBOX_GROUP, FieldType.SELECT, FieldType.MULTI_SELECT];
|
||||||
|
|
||||||
|
const fields = await this.prisma.profileField.findMany({
|
||||||
|
where: {
|
||||||
|
isActive: true,
|
||||||
|
fieldType: { in: filterableTypes },
|
||||||
|
options: { not: Prisma.JsonNull },
|
||||||
|
},
|
||||||
|
select: {
|
||||||
|
id: true,
|
||||||
|
name: true,
|
||||||
|
slug: true,
|
||||||
|
fieldType: true,
|
||||||
|
options: true,
|
||||||
|
section: {
|
||||||
|
select: {
|
||||||
|
id: true,
|
||||||
|
name: true,
|
||||||
|
slug: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
orderBy: [
|
||||||
|
{ section: { sortOrder: 'asc' } },
|
||||||
|
{ sortOrder: 'asc' },
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
// Group fields by slug to merge options from different sections (same field in different agent types)
|
||||||
|
const mergedFields = new Map<string, {
|
||||||
|
slug: string;
|
||||||
|
name: string;
|
||||||
|
fieldType: string;
|
||||||
|
options: { label: string; value: string }[];
|
||||||
|
}>();
|
||||||
|
|
||||||
|
for (const field of fields) {
|
||||||
|
const existingField = mergedFields.get(field.slug);
|
||||||
|
const fieldOptions = (field.options as { label: string; value: string }[]) || [];
|
||||||
|
|
||||||
|
if (existingField) {
|
||||||
|
// Merge options, avoiding duplicates by value
|
||||||
|
const existingValues = new Set(existingField.options.map(o => o.value));
|
||||||
|
for (const opt of fieldOptions) {
|
||||||
|
if (!existingValues.has(opt.value)) {
|
||||||
|
existingField.options.push(opt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
mergedFields.set(field.slug, {
|
||||||
|
slug: field.slug,
|
||||||
|
name: field.name,
|
||||||
|
fieldType: field.fieldType,
|
||||||
|
options: [...fieldOptions],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Array.from(mergedFields.values());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user