Skip to content

Commit f007984

Browse files
committed
fix(web): filter empty objects from entity data display
- Make isDisplayableValue recursively check if objects have displayable content, not just check if they're truthy - Prevents empty object containers like {ids: {orcid: null}} from rendering labels without content - Removes redundant "External IDs" label when ids object only contains null values after filtering
1 parent 903ab7c commit f007984

File tree

1 file changed

+27
-2
lines changed

1 file changed

+27
-2
lines changed

apps/web/src/components/EntityDataDisplay.tsx

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -170,13 +170,20 @@ interface SectionData {
170170
}
171171

172172
/**
173-
* Check if a value should be displayed (not null, undefined, empty string, or empty array)
173+
* Check if a value should be displayed (not null, undefined, empty string, empty array, or empty object)
174174
* @param value
175175
*/
176176
const isDisplayableValue = (value: unknown): boolean => {
177177
if (value === null || value === undefined) return false;
178178
if (typeof value === "string" && value.trim() === "") return false;
179179
if (Array.isArray(value) && value.length === 0) return false;
180+
// Check if object has any displayable properties
181+
if (typeof value === "object" && !Array.isArray(value)) {
182+
const entries = Object.entries(value as Record<string, unknown>);
183+
if (entries.length === 0) return false;
184+
// Recursively check if any property is displayable
185+
return entries.some(([, val]) => isDisplayableValue(val));
186+
}
180187
return true;
181188
};
182189

@@ -220,14 +227,32 @@ const groupFields = (data: Record<string, unknown>): SectionData[] => {
220227
"Other": {},
221228
};
222229

230+
// Preprocess: Remove redundant ids.openalex if it matches the main id field
231+
const mainId = typeof data.id === "string" ? data.id.toLowerCase() : null;
232+
const processedData = { ...data };
233+
if (processedData.ids && typeof processedData.ids === "object" && mainId) {
234+
const ids = processedData.ids as Record<string, unknown>;
235+
const openalexId = typeof ids.openalex === "string" ? ids.openalex.toLowerCase() : null;
236+
if (openalexId && mainId.includes(openalexId.split("/").pop() ?? "")) {
237+
// Remove openalex from ids since it's redundant with main id
238+
const { openalex: _, ...remainingIds } = ids;
239+
if (Object.keys(remainingIds).length > 0) {
240+
processedData.ids = remainingIds;
241+
} else {
242+
// Delete the key entirely if no other ids remain
243+
delete processedData.ids;
244+
}
245+
}
246+
}
247+
223248
const identifierKeys = ["id", "ids", "doi", "orcid", "issn", "ror", "mag", "openalex_id", "pmid", "pmcid"];
224249
const metricKeys = ["cited_by_count", "works_count", "h_index", "i10_index", "counts_by_year", "summary_stats", "fwci", "citation_normalized_percentile", "cited_by_percentile_year"];
225250
const relationshipKeys = ["authorships", "institutions", "concepts", "topics", "keywords", "grants", "sustainable_development_goals", "mesh", "affiliations", "last_known_institutions", "primary_location", "locations", "best_oa_location", "alternate_host_venues", "x_concepts"];
226251
const dateKeys = ["publication_date", "publication_year"];
227252
const geoKeys = ["country_code", "countries_distinct_count", "geo", "latitude", "longitude"];
228253
const basicKeys = ["display_name", "title", "type", "description", "homepage_url", "image_url", "thumbnail_url", "is_oa", "oa_status", "has_fulltext"];
229254

230-
Object.entries(data).forEach(([key, value]) => {
255+
Object.entries(processedData).forEach(([key, value]) => {
231256
// Skip hidden fields and non-displayable values
232257
if (HIDDEN_FIELDS.has(key) || !isDisplayableValue(value)) {
233258
return;

0 commit comments

Comments
 (0)