fix: admin role change, logo access, magic link validation, login help

- Add updateTeamMemberRole mutation for admins to change team member roles
- Allow any team member (not just lead) to change project logo
- Add visible "Add logo"/"Change" label under logo for discoverability
- Pre-check email existence before sending magic link (show error)
- Add "forgot which email" contact link on login page

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-05 16:37:45 +01:00
parent 67670472f7
commit 6b6f5e33f5
6 changed files with 159 additions and 46 deletions

View File

@@ -1611,6 +1611,58 @@ export const projectRouter = router({
return { success: true }
}),
/**
* Update a team member's role (admin only).
* Prevents removing the last LEAD.
*/
updateTeamMemberRole: adminProcedure
.input(
z.object({
projectId: z.string(),
userId: z.string(),
role: z.enum(['LEAD', 'MEMBER', 'ADVISOR']),
})
)
.mutation(async ({ ctx, input }) => {
const { projectId, userId, role } = input
const member = await ctx.prisma.teamMember.findUniqueOrThrow({
where: { projectId_userId: { projectId, userId } },
select: { role: true },
})
// Prevent removing the last LEAD
if (member.role === 'LEAD' && role !== 'LEAD') {
const leadCount = await ctx.prisma.teamMember.count({
where: { projectId, role: 'LEAD' },
})
if (leadCount <= 1) {
throw new TRPCError({
code: 'BAD_REQUEST',
message: 'Cannot change the role of the last team lead',
})
}
}
await ctx.prisma.teamMember.update({
where: { projectId_userId: { projectId, userId } },
data: { role },
})
await logAudit({
prisma: ctx.prisma,
userId: ctx.user.id,
action: 'UPDATE_TEAM_MEMBER_ROLE',
entityType: 'Project',
entityId: projectId,
detailsJson: { targetUserId: userId, oldRole: member.role, newRole: role },
ipAddress: ctx.ip,
userAgent: ctx.userAgent,
})
return { success: true }
}),
// =========================================================================
// BULK NOTIFICATION ENDPOINTS
// =========================================================================