feat: member profile pages with clickable links from all member lists
All checks were successful
Build and Push Docker Image / build (push) Successful in 8m51s

- Member detail page (/admin/members/[id]) now shows:
  - Profile details card (nationality, country, institution, bio)
  - Team memberships / projects with links to project pages
  - Jury groups with role (Chair/Member/Observer)
  - All roles including Applicant, Award Master, Audience in role selector
- Project detail page team members now show:
  - Nationality, institution, country inline
  - Names are clickable links to member profile pages
- Members list: names are clickable links to profile pages (all tabs)
- Applicants tab: added nationality and institution columns
- Backend: user.get includes teamMemberships and juryGroupMemberships
- Backend: project.getFullDetail includes nationality/country/institution

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-05 13:29:56 +01:00
parent c6d0f90038
commit b6ba5d7145
5 changed files with 168 additions and 15 deletions

View File

@@ -351,6 +351,20 @@ export const userRouter = router({
_count: {
select: { assignments: true, mentorAssignments: true },
},
teamMemberships: {
include: {
project: {
select: { id: true, title: true, teamName: true, competitionCategory: true },
},
},
},
juryGroupMemberships: {
include: {
juryGroup: {
select: { id: true, name: true },
},
},
},
},
})
const avatarUrl = await getUserAvatarUrl(user.profileImageKey, user.profileImageProvider)
@@ -447,7 +461,7 @@ export const userRouter = router({
id: z.string(),
email: z.string().email().optional(),
name: z.string().optional().nullable(),
role: z.enum(['SUPER_ADMIN', 'PROGRAM_ADMIN', 'AWARD_MASTER', 'JURY_MEMBER', 'MENTOR', 'OBSERVER']).optional(),
role: z.enum(['SUPER_ADMIN', 'PROGRAM_ADMIN', 'AWARD_MASTER', 'JURY_MEMBER', 'MENTOR', 'OBSERVER', 'APPLICANT', 'AUDIENCE']).optional(),
status: z.enum(['NONE', 'INVITED', 'ACTIVE', 'SUSPENDED']).optional(),
expertiseTags: z.array(z.string()).optional(),
maxAssignments: z.number().int().min(1).max(100).optional().nullable(),