Tests, UX improvements.
This commit is contained in:
parent
4e6f5d36b8
commit
0ae18f495e
63 changed files with 1384 additions and 2330 deletions
43
server/api/members/[id]/activity.get.js
Normal file
43
server/api/members/[id]/activity.get.js
Normal 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 }
|
||||
})
|
||||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
29
server/api/members/me/activity.get.js
Normal file
29
server/api/members/me/activity.get.js
Normal 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 }
|
||||
})
|
||||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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}`,
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -62,6 +62,8 @@ export default defineEventHandler(async (event) => {
|
|||
{ runValidators: false }
|
||||
)
|
||||
|
||||
logActivity(member._id, 'email_changed', { previousEmail: oldEmail })
|
||||
|
||||
return {
|
||||
success: true,
|
||||
email: newEmail,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue