Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions convex/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,12 @@ export default defineSchema({
})
),
onboardingCompletedAt: v.optional(v.float64()),
accountUiState: v.optional(
v.object({
profileCompletenessDismissedAt: v.optional(v.float64()),
profileCompletenessDismissedVersion: v.optional(v.string()),
})
),
createdAt: v.float64(),
updatedAt: v.float64(),
})
Expand Down
42 changes: 42 additions & 0 deletions convex/staff.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,48 @@ export const create = mutation({
},
});

export const updateOwnPreferences = mutation({
args: {
userId: v.string(),
profileId: v.id('lecturer_profiles'),
prefWorkingLocation: v.optional(v.string()),
prefWorkingTime: v.optional(
v.union(v.literal('am'), v.literal('pm'), v.literal('all_day'))
),
prefSpecialism: v.optional(v.string()),
prefNotes: v.optional(v.string()),
},
handler: async (ctx, args) => {
const profile = await ctx.db.get(args.profileId);

if (!profile) {
throw new Error('Profile not found');
}

if (profile.userSubject !== args.userId) {
throw new Error('You can only update your own profile preferences');
}

await ctx.db.patch(args.profileId, {
...(args.prefWorkingLocation !== undefined
? { prefWorkingLocation: args.prefWorkingLocation.trim() || undefined }
: {}),
...(args.prefWorkingTime !== undefined
? { prefWorkingTime: args.prefWorkingTime }
: {}),
...(args.prefSpecialism !== undefined
? { prefSpecialism: args.prefSpecialism.trim() || undefined }
: {}),
...(args.prefNotes !== undefined
? { prefNotes: args.prefNotes.trim() || undefined }
: {}),
updatedAt: Date.now(),
});

return { success: true };
},
});

// Update lecturer profile
export const edit = mutation({
args: {
Expand Down
34 changes: 34 additions & 0 deletions convex/users.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1428,9 +1428,15 @@ export const getAccountManagementDetails = query({
? await ctx.db.get(user.organisationId)
: null;

const linkedStaffProfile = await ctx.db
.query('lecturer_profiles')
.filter((q) => q.eq(q.field('userSubject'), args.subject))
.first();

return {
user,
organisation,
linkedStaffProfile,
};
},
});
Expand Down Expand Up @@ -1501,4 +1507,32 @@ export const updateOwnAccount = mutation({

return await ctx.db.get(user._id);
},
});

export const dismissProfileCompleteness = mutation({
args: {
subject: v.string(),
version: v.string(),
},
handler: async (ctx, args) => {
const user = await ctx.db
.query('users')
.withIndex('by_subject', (q) => q.eq('subject', args.subject))
.first();

if (!user || !user.isActive) {
throw new Error('User not found');
}

await ctx.db.patch(user._id, {
accountUiState: {
...(user.accountUiState ?? {}),
profileCompletenessDismissedAt: Date.now(),
profileCompletenessDismissedVersion: args.version,
},
updatedAt: Date.now(),
});

return { dismissed: true };
},
});
69 changes: 69 additions & 0 deletions src/app/account/actions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
'use server';

type SendPasswordResetState = {
success: boolean;
error: string | null;
};

const genericErrorMessage =
'Unable to send password reset email. Please try again.';

export async function sendPasswordResetEmail(
_previousState: SendPasswordResetState,
formData: FormData
): Promise<SendPasswordResetState> {
const email = String(formData.get('email') ?? '').trim();

if (!email) {
return {
success: false,
error: 'Email address is missing.',
};
}

const apiKey = process.env.WORKOS_API_KEY;
const clientId = process.env.WORKOS_CLIENT_ID;
const passwordResetUrl = process.env.WORKOS_PASSWORD_RESET_URL;

if (!apiKey || !clientId || !passwordResetUrl) {
return {
success: false,
error: 'Password reset is not configured.',
};
}

try {
const response = await fetch(
'https://api.workos.com/user_management/password_reset/send',
{
method: 'POST',
headers: {
Authorization: `Bearer ${apiKey}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
email,
client_id: clientId,
password_reset_url: passwordResetUrl,
}),
}
);

if (!response.ok) {
return {
success: false,
error: genericErrorMessage,
};
}

return {
success: true,
error: null,
};
} catch {
return {
success: false,
error: genericErrorMessage,
};
}
}
Loading
Loading