feat(admin): add member onboarding alert detectors
This commit is contained in:
parent
d3a961f765
commit
824364d526
2 changed files with 192 additions and 2 deletions
|
|
@ -34,6 +34,86 @@ export function computeSignature(ids) {
|
|||
return hash.digest('hex')
|
||||
}
|
||||
|
||||
// Alert functions land here in tasks 4–7.
|
||||
function memberItem(member, sublabel) {
|
||||
return {
|
||||
id: String(member._id),
|
||||
label: member.name,
|
||||
sublabel: sublabel ?? member.email,
|
||||
href: `/admin/members/${member._id}`
|
||||
}
|
||||
}
|
||||
|
||||
export async function detectSlackInviteFailed() {
|
||||
await connectDB()
|
||||
const members = await Member
|
||||
.find({ slackInviteStatus: 'failed' })
|
||||
.select('name email')
|
||||
.lean()
|
||||
return {
|
||||
type: 'slack_invite_failed',
|
||||
severity: 'critical',
|
||||
title: 'Slack invites failed',
|
||||
items: members.map((m) => memberItem(m))
|
||||
}
|
||||
}
|
||||
|
||||
export async function detectNoSlackHandleAfterWeek() {
|
||||
await connectDB()
|
||||
const cutoff = daysAgo(ALERT_THRESHOLDS.NO_SLACK_DAYS)
|
||||
const members = await Member
|
||||
.find({
|
||||
status: 'active',
|
||||
createdAt: { $lte: cutoff },
|
||||
$or: [
|
||||
{ slackUserId: { $exists: false } },
|
||||
{ slackUserId: null },
|
||||
{ slackUserId: '' }
|
||||
]
|
||||
})
|
||||
.select('name email createdAt')
|
||||
.lean()
|
||||
return {
|
||||
type: 'no_slack_handle_week',
|
||||
severity: 'attention',
|
||||
title: 'Active members without a Slack handle',
|
||||
items: members.map((m) =>
|
||||
memberItem(m, `${daysSince(m.createdAt)} days since joining`)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export async function detectStuckPendingPayment() {
|
||||
await connectDB()
|
||||
const cutoff = daysAgo(ALERT_THRESHOLDS.STUCK_PAYMENT_DAYS)
|
||||
const members = await Member
|
||||
.find({
|
||||
status: 'pending_payment',
|
||||
createdAt: { $lte: cutoff }
|
||||
})
|
||||
.select('name email createdAt')
|
||||
.lean()
|
||||
return {
|
||||
type: 'stuck_pending_payment',
|
||||
severity: 'attention',
|
||||
title: 'Members stuck in pending payment',
|
||||
items: members.map((m) =>
|
||||
memberItem(m, `${daysSince(m.createdAt)} days stuck`)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export async function detectSuspendedMembers() {
|
||||
await connectDB()
|
||||
const members = await Member
|
||||
.find({ status: 'suspended' })
|
||||
.select('name email')
|
||||
.lean()
|
||||
return {
|
||||
type: 'member_suspended',
|
||||
severity: 'attention',
|
||||
title: 'Suspended members',
|
||||
items: members.map((m) => memberItem(m))
|
||||
}
|
||||
}
|
||||
|
||||
// Aggregator lands in task 8.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue