Tests, UX improvements.

This commit is contained in:
Jennie Robinson Faber 2026-04-05 14:25:29 +01:00
parent 4e6f5d36b8
commit 0ae18f495e
63 changed files with 1384 additions and 2330 deletions

View file

@ -0,0 +1,43 @@
import Member from '../../../models/member.js'
import ActivityLog from '../../../models/activityLog.js'
import { connectDB } from '../../../utils/mongoose.js'
export default defineEventHandler(async (event) => {
await connectDB()
const id = getRouterParam(event, 'id')
const member = await Member.findOne({
_id: id,
showInDirectory: true,
status: 'active'
}).lean()
if (!member) {
throw createError({ statusCode: 404, statusMessage: 'Member not found' })
}
const query = getQuery(event)
const limit = Math.min(parseInt(query.limit) || 5, 20)
const before = query.before ? new Date(query.before) : null
const filter = {
member: member._id,
visibility: 'public'
}
if (before) filter.timestamp = { $lt: before }
const entries = await ActivityLog.find(filter)
.sort({ timestamp: -1 })
.limit(limit + 1)
.lean()
const hasMore = entries.length > limit
if (hasMore) entries.pop()
const nextCursor = hasMore && entries.length
? entries[entries.length - 1].timestamp.toISOString()
: null
return { entries, hasMore, nextCursor }
})

View file

@ -62,6 +62,10 @@ export default defineEventHandler(async (event) => {
{ runValidators: false }
);
logActivity(member._id, 'subscription_cancelled', {
effectiveDate: new Date().toISOString()
})
return {
success: true,
message: "Subscription cancelled successfully",

View file

@ -100,10 +100,15 @@ export default defineEventHandler(async (event) => {
const member = new Member(validatedData)
await member.save()
// Log member joined
logActivity(member._id, 'member_joined', {
circle: member.circle
}, { timestamp: member.createdAt })
// Send Slack invitation for new members
await inviteToSlack(member)
// TODO: Process payment with Helcim if not free tier
if (requiresPayment(validatedData.contributionTier)) {
// Payment processing will be added here
@ -112,6 +117,10 @@ export default defineEventHandler(async (event) => {
// Send welcome email (non-blocking)
try {
await sendWelcomeEmail(member)
logActivity(member._id, 'email_sent', {
emailType: 'welcome',
subject: 'Welcome to Ghost Guild'
})
} catch (emailError) {
console.error('Failed to send welcome email:', emailError)
}

View file

@ -0,0 +1,29 @@
import ActivityLog from '../../../models/activityLog.js'
export default defineEventHandler(async (event) => {
const member = await requireAuth(event)
const query = getQuery(event)
const limit = Math.min(parseInt(query.limit) || 20, 50)
const before = query.before ? new Date(query.before) : null
const filter = {
member: member._id,
visibility: { $in: ['member', 'public'] }
}
if (before) filter.timestamp = { $lt: before }
const entries = await ActivityLog.find(filter)
.sort({ timestamp: -1 })
.limit(limit + 1)
.lean()
const hasMore = entries.length > limit
if (hasMore) entries.pop()
const nextCursor = hasMore && entries.length
? entries[entries.length - 1].timestamp.toISOString()
: null
return { entries, hasMore, nextCursor }
})

View file

@ -75,6 +75,14 @@ export default defineEventHandler(async (event) => {
})
}
if (body.enabled) {
logActivity(member._id, 'peer_support_enabled', {
topics: [...(body.skillTopics || []), ...(body.supportTopics || [])]
})
} else {
logActivity(member._id, 'peer_support_disabled', {})
}
return {
success: true,
peerSupport: updated.peerSupport,

View file

@ -80,6 +80,12 @@ export default defineEventHandler(async (event) => {
});
}
// Log which fields were updated
const changedFields = Object.keys(body).filter(k => body[k] !== undefined && !k.endsWith('Privacy'))
if (changedFields.length) {
logActivity(memberId, 'profile_updated', { fields: changedFields })
}
// Return sanitized member data
return {
id: member._id,

View file

@ -13,12 +13,19 @@ export default defineEventHandler(async (event) => {
return { success: true, message: 'Already in this circle' }
}
const oldCircle = member.circle
await Member.findByIdAndUpdate(
member._id,
{ $set: { circle: body.circle } },
{ runValidators: false }
)
logActivity(member._id, 'circle_changed', {
from: oldCircle,
to: body.circle
})
return {
success: true,
message: `Circle updated to ${body.circle}`,

View file

@ -26,6 +26,11 @@ export default defineEventHandler(async (event) => {
};
}
// Log contribution change (fire-and-forget, at the top so it logs regardless of which case path executes)
const logContributionChange = () => {
logActivity(member._id, 'contribution_changed', { from: oldTier, to: newTier })
}
const helcimToken = config.helcimApiToken;
const oldRequiresPayment = requiresPayment(oldTier);
const newRequiresPayment = requiresPayment(newTier);
@ -160,6 +165,8 @@ export default defineEventHandler(async (event) => {
{ runValidators: false }
);
logContributionChange()
return {
success: true,
message: "Successfully upgraded to paid tier",
@ -216,6 +223,8 @@ export default defineEventHandler(async (event) => {
{ runValidators: false }
);
logContributionChange()
return {
success: true,
message: "Successfully downgraded to free tier",
@ -279,6 +288,8 @@ export default defineEventHandler(async (event) => {
{ runValidators: false }
);
logContributionChange()
return {
success: true,
message: "Successfully updated contribution level",
@ -300,6 +311,8 @@ export default defineEventHandler(async (event) => {
{ runValidators: false }
);
logContributionChange()
return {
success: true,
message: "Successfully updated contribution level",

View file

@ -62,6 +62,8 @@ export default defineEventHandler(async (event) => {
{ runValidators: false }
)
logActivity(member._id, 'email_changed', { previousEmail: oldEmail })
return {
success: true,
email: newEmail,