import { requireAuth } from '../../utils/auth.js' import { validateBody } from '../../utils/validateBody.js' import { onboardingTrackSchema } from '../../utils/schemas.js' import Member from '../../models/member.js' import BoardPost from '../../models/boardPost.js' import { logActivity } from '../../utils/activityLog.js' export default defineEventHandler(async (event) => { const member = await requireAuth(event) const { goal, skip } = await validateBody(event, onboardingTrackSchema) // Already graduated — no-op if (member.onboarding?.completedAt) { return { success: true } } // Skip path: mark suggestion as skipped so the UI advances. if (skip) { await Member.findByIdAndUpdate(member._id, { $set: { [`onboarding.skipped.${skip}`]: true }, }) return { success: true } } // Idempotent — already tracked if (member.onboarding?.[goal]) { return { success: true } } // Set the boolean await Member.findByIdAndUpdate(member._id, { $set: { [`onboarding.${goal}`]: true }, }) // Log the individual goal completion await logActivity(member._id, 'member_onboarding_goal_completed', { goal }, { visibility: 'admin' }) // Must have at least one board post to graduate const hasPosted = await BoardPost.exists({ author: member._id }) // Graduation check — atomic so concurrent requests can't double-graduate const graduated = hasPosted ? await Member.findOneAndUpdate( { _id: member._id, 'onboarding.completedAt': null, 'onboarding.eventPageVisited': true, 'onboarding.boardPageVisited': true, 'onboarding.wikiClicked': true, 'craftTags.0': { $exists: true }, }, { $set: { 'onboarding.completedAt': new Date() } }, { new: true } ) : null if (graduated) { await logActivity(member._id, 'member_onboarding_completed', {}, { visibility: 'admin' }) } return { success: true, graduated: !!graduated } })