Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 77 additions & 13 deletions src/app/public/events/[eventId]/_components/past-event-detail.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -806,6 +806,35 @@ export const PastEventDetail = ({ eventId }: EventDetailProps) => {
const isProjectsLoading = projectsQuery.isLoading || projectsQuery.isFetching;
const isProjectsError = projectsQuery.isError;

const approvedProjects = useMemo(
() => projects.filter((project) => String(project.state ?? '').toUpperCase() === 'APPROVED'),
[projects],
);

const approvedProjectLookup = useMemo(() => {
const byId = new Set<string>();
const byCode = new Set<string>();
const byName = new Set<string>();

approvedProjects.forEach((project) => {
if (project.id !== undefined && project.id !== null) {
byId.add(String(project.id));
}

const projectCode = normalizeText(String(project.projectCode ?? project.eventNumber ?? ''));
if (projectCode) {
byCode.add(projectCode);
}

const projectName = normalizeText(String(project.name ?? ''));
if (projectName) {
byName.add(projectName);
}
});

return { byId, byCode, byName };
}, [approvedProjects]);

const rankingConfigQuery = useRankingConfig({ eventId: event?.id, queryConfig: { enabled: Boolean(event?.id) } });

const configVisibleFlag = (() => {
Expand Down Expand Up @@ -853,27 +882,27 @@ export const PastEventDetail = ({ eventId }: EventDetailProps) => {
});

// Finally, add categories from projects
projects.forEach((project) => {
approvedProjects.forEach((project) => {
const id = Number(project?.categoryId);
if (Number.isFinite(id) && !categoryMap.has(id)) {
categoryMap.set(id, `Categoría ${id}`);
}
});

return Array.from(categoryMap, ([id, name]) => ({ id, name }));
}, [event?.categories, event?.awards, projects]);
}, [event?.categories, event?.awards, approvedProjects]);

// Filter out categories that have no projects to avoid showing empty category pills
const visibleCategories = useMemo(() => {
if (!projects || projects.length === 0) return [];
if (!approvedProjects || approvedProjects.length === 0) return [];
const projectCategoryIds = new Set<number>();
for (const p of projects) {
for (const p of approvedProjects) {
const cid = Number(p?.categoryId);
if (Number.isFinite(cid) && cid > 0) projectCategoryIds.add(cid);
}

return categories.filter((c) => projectCategoryIds.has(c.id));
}, [categories, projects]);
}, [categories, approvedProjects]);



Expand Down Expand Up @@ -903,17 +932,40 @@ export const PastEventDetail = ({ eventId }: EventDetailProps) => {
const rankingQuery = usePublicEventRankings({
eventId: event?.id,
categoryId: selectedCategoryId,
state: 'APPROVED',
queryConfig: { enabled: Boolean(event?.id) && Boolean(isPublicRankingEnabled) },
});

const rankingByCategory = useMemo(() => {
// Prefer server-provided public rankings when available
const serverRows = rankingQuery.data?.data ?? [];
const serverRows = (rankingQuery.data?.data ?? []).filter((row) => {
const projectId = row.projectId !== undefined && row.projectId !== null ? String(row.projectId) : undefined;
if (projectId && approvedProjectLookup.byId.has(projectId)) {
return true;
}

const projectCode = normalizeText(String(row.projectCode ?? ''));
if (projectCode && approvedProjectLookup.byCode.has(projectCode)) {
return true;
}

const projectName = normalizeText(String(row.projectName ?? ''));
if (projectName && approvedProjectLookup.byName.has(projectName)) {
return true;
}

return false;
});
if (Array.isArray(serverRows) && serverRows.length > 0) {
const map = new Map<number, RankedProject[]>();
for (const row of serverRows) {
const catId = Number(row.category);
const normalizedCategoryId = Number.isFinite(catId) ? catId : 0;
const normalizedCategoryId = Number.isFinite(Number(row.categoryId))
? Number(row.categoryId)
: (() => {
const categoryLabel = normalizeText(String(row.category ?? ''));
const matchedCategory = categories.find((category) => normalizeText(category.name) === categoryLabel);
return matchedCategory?.id ?? selectedCategoryId ?? 0;
})();
const list = map.get(normalizedCategoryId) ?? [];
list.push({
position: row.position,
Expand Down Expand Up @@ -941,19 +993,31 @@ export const PastEventDetail = ({ eventId }: EventDetailProps) => {
// Fallback to awards-based ranking when server data not available
if (!event?.awards?.length) return new Map<number, RankedProject[]>();
return toAwardRanking(event.awards, projects, categories[0]?.id);
}, [rankingQuery.data, event?.awards, categories, projects]);
}, [approvedProjectLookup, rankingQuery.data, event?.awards, categories, approvedProjects]);

const rankingCategories = useMemo(() => {
const ids = Array.from(rankingByCategory.keys());
if (ids.length === 0) return [];

return ids.map((id) => ({
id,
name: categories.find((category) => category.id === id)?.name ?? `Categoría ${id}`,
}));
}, [rankingByCategory, categories]);

const displayCategories = visibleCategories.length > 0 ? visibleCategories : rankingCategories;

const isRankingLoading = rankingQuery.isLoading || rankingConfigQuery.isLoading;

const shouldRenderWinners = useMemo(() => {
if (!isPublicRankingEnabled) return false;
if (!visibleCategories || visibleCategories.length === 0) return false;
if (!displayCategories || displayCategories.length === 0) return false;
// Only render winners if at least one visible category has ranking entries
for (const c of visibleCategories) {
for (const c of displayCategories) {
if ((rankingByCategory.get(c.id)?.length ?? 0) > 0) return true;
}
return false;
}, [isPublicRankingEnabled, visibleCategories, rankingByCategory]);
}, [isPublicRankingEnabled, displayCategories, rankingByCategory]);

const eventTypeLabel = useMemo(
() => normalizeEventType(event?.eventType),
Expand Down Expand Up @@ -1129,7 +1193,7 @@ export const PastEventDetail = ({ eventId }: EventDetailProps) => {
<>
<PastEventWinnersTop
rankingByCategory={rankingByCategory}
categories={visibleCategories}
categories={displayCategories}
isExposition={isExposition}
visiblePositions={visibleRankingPositions}
visibleScore={visibleRankingScore}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,7 @@ export function PastEventWinnersTop({
const appliedLimit = Number(visiblePositions) || 0;

const availableWinners = useMemo(() => {
if (appliedLimit === 0) return [];
if (appliedLimit === 0) return allWinners;
return allWinners.filter((w) => w.position >= 1 && w.position <= appliedLimit);
}, [allWinners, appliedLimit]);

Expand Down
79 changes: 75 additions & 4 deletions src/features/events/components/past-event-dashboard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -101,9 +101,39 @@ export const PastEventDashboard = ({ event, onBack }: Props) => {
const rankingQuery = useEventRankings({
eventId: event.id,
categoryId: selectedCategoryId,
state: 'APPROVED',
queryConfig: { enabled: canFetchRanking },
});

const approvedProjects = useMemo(
() => projects.filter((project) => String(project.state ?? '').toUpperCase() === 'APPROVED'),
[projects],
);

const approvedProjectLookup = useMemo(() => {
const byId = new Set<string>();
const byCode = new Set<string>();
const byName = new Set<string>();

approvedProjects.forEach((project) => {
if (project.id !== undefined && project.id !== null) {
byId.add(String(project.id));
}

const projectCode = normalizeText(String(project.projectCode ?? project.eventNumber ?? ''));
if (projectCode) {
byCode.add(projectCode);
}

const projectName = normalizeText(String(project.name ?? ''));
if (projectName) {
byName.add(projectName);
}
});

return { byId, byCode, byName };
}, [approvedProjects]);

const chartData = useMemo(() => {
const participantsByCategory = new Map<number, Set<string>>();
const projectsByCategory = new Map<number, number>();
Expand Down Expand Up @@ -303,6 +333,7 @@ export const PastEventDashboard = ({ event, onBack }: Props) => {
const { blob, fileName } = await downloadEventRankingsReport({
eventId: event.id,
categoryId: selectedCategoryId,
state: 'APPROVED',
});

const url = URL.createObjectURL(blob);
Expand Down Expand Up @@ -340,9 +371,47 @@ export const PastEventDashboard = ({ event, onBack }: Props) => {
visibleScore: existingRankingConfig?.visibleScore ?? fetchedRankingConfig?.visibleScore ?? true,
});
}, [event, rankingConfigQuery.data]);
const rankingEntries = useMemo(() => {
const rankingEntries = useMemo<any[]>(() => {
const term = normalizeText(rankingSearch);
return rankingRows
const approvedRankingRows = rankingRows
.map((entry) => {
const projectId = entry.projectId !== undefined && entry.projectId !== null ? String(entry.projectId) : undefined;
const matchedProject = approvedProjects.find((project) => {
if (projectId && String(project.id) === projectId) return true;

const projectCode = normalizeText(String(project.projectCode ?? project.eventNumber ?? ''));
if (projectCode && projectCode === normalizeText(String(entry.projectCode ?? ''))) return true;

const projectName = normalizeText(String(project.name ?? ''));
return projectName && projectName === normalizeText(String(entry.projectName ?? ''));
});

const projectStats = matchedProject?.id !== undefined ? projectStatsById.get(String(matchedProject.id)) : undefined;
const fallbackEvaluationCount = typeof projectStats?.evaluationCount === 'number' ? projectStats.evaluationCount : undefined;

return {
...entry,
evaluationCount: entry.evaluationCount > 0 ? entry.evaluationCount : (fallbackEvaluationCount ?? entry.evaluationCount ?? 0),
};
})
.filter((entry) => {
const projectId = entry.projectId !== undefined && entry.projectId !== null ? String(entry.projectId) : undefined;
if (projectId && approvedProjectLookup.byId.has(projectId)) {
return true;
}

const projectCode = normalizeText(String(entry.projectCode ?? ''));
if (projectCode && approvedProjectLookup.byCode.has(projectCode)) {
return true;
}

const projectName = normalizeText(String(entry.projectName ?? ''));
if (projectName && approvedProjectLookup.byName.has(projectName)) {
return true;
}

return false;
})
.filter((entry) => {
if (!term) return true;

Expand All @@ -355,9 +424,11 @@ export const PastEventDashboard = ({ event, onBack }: Props) => {
].join(' ')).includes(term);
})
.sort((a, b) => a.position - b.position);
}, [rankingRows, rankingSearch]);

const visibleRankingEntries = useMemo(() => {
return approvedRankingRows;
}, [approvedProjects, approvedProjectLookup, projectStatsById, rankingRows, rankingSearch]);

const visibleRankingEntries = useMemo<any[]>(() => {
if (!visiblePositions || visiblePositions <= 0) {
return rankingEntries;
}
Expand Down
Loading