refactor: centralize profile field retrieval and filtering logic in buildFieldValuesResponse helper

This commit is contained in:
pradeepkumar
2026-04-24 12:55:55 +05:30
parent 360746c868
commit 9ae2f6516b

View File

@@ -1099,128 +1099,82 @@ export class AgentsService {
} }
async getFieldValues(userId: string) { async getFieldValues(userId: string) {
// Get agent profile
const profile = await this.prisma.agentProfile.findUnique({ const profile = await this.prisma.agentProfile.findUnique({
where: { userId }, where: { userId },
select: { id: true, agentTypeId: true },
}); });
if (!profile) { if (!profile) {
throw new NotFoundException('Agent profile not found'); throw new NotFoundException('Agent profile not found');
} }
// Get all field values for this profile, ordered by admin-defined section + field sort order return this.buildFieldValuesResponse(profile);
const fieldValues = await this.prisma.agentProfileFieldValue.findMany({
where: { agentProfileId: profile.id },
include: {
field: {
select: {
id: true,
slug: true,
name: true,
fieldType: true,
options: true,
section: {
select: {
id: true,
slug: true,
name: true,
},
},
},
},
},
orderBy: [
{ field: { section: { sortOrder: 'asc' } } },
{ field: { sortOrder: 'asc' } },
],
});
// Helper to resolve a raw value to its option label (for SELECT/RADIO/MULTI_SELECT fields)
// Returns `null` when no matching option is found — the caller can treat those as orphans.
const resolveLabel = (rawValue: any, options: any): any => {
if (!options || !Array.isArray(options) || options.length === 0) return rawValue;
const lookup = (val: any): string | null => {
const match = (options as any[]).find((o: any) => o.value === val || o.value === String(val));
return match?.label ?? null;
};
if (Array.isArray(rawValue)) return rawValue.map(lookup);
return lookup(rawValue);
};
// Transform to a more usable format. For enumerated field types, drop any array entry
// whose value no longer resolves to a current option so stale selections never leak through.
const transformedValues = fieldValues.map((fv) => {
const rawValue = this.extractValue(fv);
let value: unknown = rawValue;
let valueLabel: unknown = resolveLabel(rawValue, fv.field.options);
const isEnum =
fv.field.fieldType === 'CHECKBOX_GROUP' ||
fv.field.fieldType === 'MULTI_SELECT' ||
fv.field.fieldType === 'SELECT' ||
fv.field.fieldType === 'RADIO';
if (isEnum && Array.isArray(value) && Array.isArray(valueLabel)) {
const pairs = (value as unknown[]).map((v, i) => ({ v, l: (valueLabel as unknown[])[i] }))
.filter((p) => p.l !== null && p.l !== undefined);
value = pairs.map((p) => p.v);
valueLabel = pairs.map((p) => p.l);
}
return {
fieldSlug: fv.field.slug,
fieldName: fv.field.name,
fieldType: fv.field.fieldType,
sectionSlug: fv.field.section.slug,
sectionName: fv.field.section.name,
value,
valueLabel,
};
});
return {
agentProfileId: profile.id,
fieldValues: transformedValues,
};
} }
async getFieldValuesByAgentId(agentId: string) { async getFieldValuesByAgentId(agentId: string) {
// Get agent profile by ID
const profile = await this.prisma.agentProfile.findUnique({ const profile = await this.prisma.agentProfile.findUnique({
where: { id: agentId }, where: { id: agentId },
select: { id: true, agentTypeId: true },
}); });
if (!profile) { if (!profile) {
throw new NotFoundException('Agent profile not found'); throw new NotFoundException('Agent profile not found');
} }
// Get all field values for this profile, ordered by admin-defined section + field sort order return this.buildFieldValuesResponse(profile);
const fieldValues = await this.prisma.agentProfileFieldValue.findMany({ }
where: { agentProfileId: profile.id },
include: { // Returns one entry per field applicable to this agent's type (global sections +
field: { // sections linked to the agent type). Fields without a saved value come back with
select: { // `value: null` and `valueLabel: null` so the frontend always has the admin-configured
id: true, // labels available.
slug: true, private async buildFieldValuesResponse(profile: {
name: true, id: string;
fieldType: true, agentTypeId: string | null;
options: true, }) {
section: { const sectionFilter: Prisma.ProfileSectionWhereInput = {
select: { isActive: true,
id: true, OR: [
slug: true, { isGlobal: true },
name: true, ...(profile.agentTypeId
}, ? [{ agentTypeSections: { some: { agentTypeId: profile.agentTypeId } } }]
}, : []),
}, ],
};
const fields = await this.prisma.profileField.findMany({
where: {
isActive: true,
section: sectionFilter,
},
select: {
id: true,
slug: true,
name: true,
fieldType: true,
options: true,
section: {
select: { id: true, slug: true, name: true },
}, },
}, },
orderBy: [ orderBy: [
{ field: { section: { sortOrder: 'asc' } } }, { section: { sortOrder: 'asc' } },
{ field: { sortOrder: 'asc' } }, { sortOrder: 'asc' },
], ],
}); });
const savedValues = await this.prisma.agentProfileFieldValue.findMany({
where: {
agentProfileId: profile.id,
fieldId: { in: fields.map((f) => f.id) },
},
});
const savedByFieldId = new Map(savedValues.map((v) => [v.fieldId, v]));
// Helper to resolve a raw value to its option label (for SELECT/RADIO/MULTI_SELECT fields) // Helper to resolve a raw value to its option label (for SELECT/RADIO/MULTI_SELECT fields)
// Returns `null` when no matching option is found — the caller can treat those as orphans. // Returns `null` when no matching option is found — the caller can treat those as orphans.
const resolveLabel = (rawValue: any, options: any): any => { const resolveLabel = (rawValue: any, options: any): any => {
if (rawValue === null || rawValue === undefined) return rawValue;
if (!options || !Array.isArray(options) || options.length === 0) return rawValue; if (!options || !Array.isArray(options) || options.length === 0) return rawValue;
const lookup = (val: any): string | null => { const lookup = (val: any): string | null => {
const match = (options as any[]).find((o: any) => o.value === val || o.value === String(val)); const match = (options as any[]).find((o: any) => o.value === val || o.value === String(val));
@@ -1230,31 +1184,33 @@ export class AgentsService {
return lookup(rawValue); return lookup(rawValue);
}; };
// Transform to a more usable format. For enumerated field types, drop any array entry const transformedValues = fields.map((field) => {
// whose value no longer resolves to a current option so stale selections never leak through. const saved = savedByFieldId.get(field.id);
const transformedValues = fieldValues.map((fv) => { const rawValue = saved
const rawValue = this.extractValue(fv); ? this.extractValue({ ...saved, field: { fieldType: field.fieldType } })
let value: unknown = rawValue; : null;
let valueLabel: unknown = resolveLabel(rawValue, fv.field.options); let value: unknown = rawValue ?? null;
let valueLabel: unknown = resolveLabel(rawValue, field.options);
const isEnum = const isEnum =
fv.field.fieldType === 'CHECKBOX_GROUP' || field.fieldType === 'CHECKBOX_GROUP' ||
fv.field.fieldType === 'MULTI_SELECT' || field.fieldType === 'MULTI_SELECT' ||
fv.field.fieldType === 'SELECT' || field.fieldType === 'SELECT' ||
fv.field.fieldType === 'RADIO'; field.fieldType === 'RADIO';
if (isEnum && Array.isArray(value) && Array.isArray(valueLabel)) { if (isEnum && Array.isArray(value) && Array.isArray(valueLabel)) {
const pairs = (value as unknown[]).map((v, i) => ({ v, l: (valueLabel as unknown[])[i] })) const pairs = (value as unknown[])
.map((v, i) => ({ v, l: (valueLabel as unknown[])[i] }))
.filter((p) => p.l !== null && p.l !== undefined); .filter((p) => p.l !== null && p.l !== undefined);
value = pairs.map((p) => p.v); value = pairs.map((p) => p.v);
valueLabel = pairs.map((p) => p.l); valueLabel = pairs.map((p) => p.l);
} }
return { return {
fieldSlug: fv.field.slug, fieldSlug: field.slug,
fieldName: fv.field.name, fieldName: field.name,
fieldType: fv.field.fieldType, fieldType: field.fieldType,
sectionSlug: fv.field.section.slug, sectionSlug: field.section.slug,
sectionName: fv.field.section.name, sectionName: field.section.name,
value, value,
valueLabel, valueLabel: valueLabel ?? null,
}; };
}); });