Implement Prototype 1 improvements: unified members, project filters, audit expansion, filtering rounds, special awards
- Unified Member Management: merge /admin/users and /admin/mentors into /admin/members with role tabs, search, pagination - Project List Filters: add search, multi-status filter, round/category/country selects, boolean toggles, URL persistence - Audit Log Expansion: track logins, round state changes, evaluation submissions, file access, role changes via shared logAudit utility - Founding Date Field: add foundedAt to Project model with CSV import support - Filtering Round System: configurable rules (field-based, document check, AI screening), execution engine, results review with override/reinstate - Special Awards System: named awards with eligibility criteria, dedicated jury, PICK_WINNER/RANKED/SCORED voting modes, AI eligibility - Dashboard resilience: wrap heavy queries in try-catch to prevent error boundary on transient DB failures - Reusable pagination component extracted to src/components/shared/pagination.tsx - Old /admin/users and /admin/mentors routes redirect to /admin/members - Prisma migration for all schema additions (additive, no data loss) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -79,6 +79,17 @@ export const { handlers, auth, signIn, signOut } = NextAuth({
|
||||
},
|
||||
})
|
||||
|
||||
// Log invitation accepted
|
||||
await prisma.auditLog.create({
|
||||
data: {
|
||||
userId: user.id,
|
||||
action: 'INVITATION_ACCEPTED',
|
||||
entityType: 'User',
|
||||
entityId: user.id,
|
||||
detailsJson: { email: user.email, role: user.role },
|
||||
},
|
||||
}).catch(() => {})
|
||||
|
||||
return {
|
||||
id: user.id,
|
||||
email: user.email,
|
||||
@@ -124,6 +135,17 @@ export const { handlers, auth, signIn, signOut } = NextAuth({
|
||||
current.count = 0
|
||||
}
|
||||
failedAttempts.set(email, current)
|
||||
|
||||
// Log failed login
|
||||
await prisma.auditLog.create({
|
||||
data: {
|
||||
userId: null,
|
||||
action: 'LOGIN_FAILED',
|
||||
entityType: 'User',
|
||||
detailsJson: { email, reason: !user ? 'user_not_found' : user.status === 'SUSPENDED' ? 'suspended' : 'no_password' },
|
||||
},
|
||||
}).catch(() => {})
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
@@ -138,6 +160,18 @@ export const { handlers, auth, signIn, signOut } = NextAuth({
|
||||
current.count = 0
|
||||
}
|
||||
failedAttempts.set(email, current)
|
||||
|
||||
// Log failed login
|
||||
await prisma.auditLog.create({
|
||||
data: {
|
||||
userId: user.id,
|
||||
action: 'LOGIN_FAILED',
|
||||
entityType: 'User',
|
||||
entityId: user.id,
|
||||
detailsJson: { email, reason: 'invalid_password' },
|
||||
},
|
||||
}).catch(() => {})
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
@@ -228,6 +262,17 @@ export const { handlers, auth, signIn, signOut } = NextAuth({
|
||||
}).catch(() => {
|
||||
// Ignore errors from updating last login
|
||||
})
|
||||
|
||||
// Log successful login
|
||||
await prisma.auditLog.create({
|
||||
data: {
|
||||
userId: user.id as string,
|
||||
action: 'LOGIN_SUCCESS',
|
||||
entityType: 'User',
|
||||
entityId: user.id as string,
|
||||
detailsJson: { method: account?.provider || 'unknown', email: user.email },
|
||||
},
|
||||
}).catch(() => {})
|
||||
}
|
||||
|
||||
return true
|
||||
|
||||
Reference in New Issue
Block a user