invite button and can navigate to submissions (no submissions will appear yet)

This commit is contained in:
Ra
2025-08-25 21:56:45 -07:00
parent 39b93237fb
commit 772d1d4c10
18 changed files with 334 additions and 132 deletions

View File

@@ -423,10 +423,35 @@ exports.createInvitation = onRequest({ cors: true }, async (req, res) => {
// Generate invite links
const baseUrl = process.env.CLIENT_URL || 'http://localhost:5173';
const inviteLink = `${baseUrl}/#/employee-form/${code}`;
const emailLink = `mailto:${email}?subject=You're invited to join our organization&body=Hi ${name},%0A%0AYou've been invited to complete a questionnaire for our organization. Please click the link below to get started:%0A%0A${inviteLink}%0A%0AThis link will expire in 7 days.%0A%0AThank you!`;
try {
if (!!process.env.SENDGRID_API_KEY) {
await fetch('https://api.sendgrid.com/v3/mail/send', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${process.env.SENDGRID_API_KEY}`,
},
body: JSON.stringify({
personalizations: [
{
to: [{ email }],
dynamic_template_data: {
name,
inviteLink,
},
},
],
from: { email: 'no-reply@auditly.com', name: 'Auditly' },
template_id: process.env.SENDGRID_TEMPLATE_ID,
}),
});
console.log(`📧 Invitation sent to ${email} (${name}) with code: ${code}`);
}
} catch (error) {
console.error("SendGrid email error:", error);
}
// In production, send actual invitation email
console.log(`📧 Invitation sent to ${email} (${name}) with code: ${code}`);
console.log(`📧 Invite link: ${inviteLink}`);
res.json({
@@ -434,7 +459,6 @@ exports.createInvitation = onRequest({ cors: true }, async (req, res) => {
code,
employee,
inviteLink,
emailLink,
message: "Invitation sent successfully",
});
} catch (error) {
@@ -605,7 +629,7 @@ exports.submitEmployeeAnswers = onRequest({ cors: true }, async (req, res) => {
const inviteDoc = inviteSnapshot.docs[0];
const invite = inviteDoc.data();
// Check if expired
if (Date.now() > invite.expiresAt) {
return res.status(400).json({ error: "Invitation has expired" });
@@ -613,21 +637,21 @@ exports.submitEmployeeAnswers = onRequest({ cors: true }, async (req, res) => {
finalOrgId = invite.orgId;
finalEmployeeId = invite.employee.id;
// Consume the invitation now
await inviteDoc.ref.update({
status: "consumed",
consumedBy: finalEmployeeId,
consumedAt: Date.now(),
});
// Add employee to organization if not already added
const employeeRef = db
.collection("orgs")
.doc(finalOrgId)
.collection("employees")
.doc(finalEmployeeId);
const employeeDoc = await employeeRef.get();
if (!employeeDoc.exists) {
await employeeRef.set({
@@ -682,13 +706,13 @@ exports.submitEmployeeAnswers = onRequest({ cors: true }, async (req, res) => {
.collection("employees")
.doc(finalEmployeeId)
.get();
const employeeData = employeeDoc.exists ? employeeDoc.data() : null;
// Get company onboarding data for LLM context
const orgDoc = await db.collection("orgs").doc(finalOrgId).get();
const orgData = orgDoc.exists ? orgDoc.data() : {};
// Prepare company context (onboarding data)
const companyContext = {
name: orgData.name,
@@ -699,7 +723,7 @@ exports.submitEmployeeAnswers = onRequest({ cors: true }, async (req, res) => {
size: orgData.size,
onboardingData: orgData // Include all org data for comprehensive context
};
// Prepare submission data
const submissionData = {
employeeId: finalEmployeeId,
@@ -707,7 +731,7 @@ exports.submitEmployeeAnswers = onRequest({ cors: true }, async (req, res) => {
submittedAt: Date.now(),
status: "completed"
};
// Generate the report using the existing function logic
let report;
if (openai) {
@@ -730,7 +754,7 @@ ${JSON.stringify(companyContext, null, 2)}
Generate a detailed report that:
1. Evaluates how well the employee aligns with company values and culture
2. Assesses their role performance and output
3. Identifies behavioral insights and work patterns
3. Identifies behavioral insights and work patterns
4. Highlights strengths and areas for improvement
5. Provides specific recommendations for growth
6. Suggests opportunities that align with company goals
@@ -835,18 +859,18 @@ Be thorough, professional, and focus on actionable insights.
},
};
}
// Store the report in Firestore
const reportRef = db
.collection("orgs")
.doc(finalOrgId)
.collection("reports")
.doc(finalEmployeeId);
await reportRef.set(report);
console.log(`Employee report generated and stored for ${finalEmployeeId} in org ${finalOrgId}`);
} catch (reportError) {
console.error("Failed to generate employee report:", reportError);
// Don't fail the submission if report generation fails
@@ -1574,8 +1598,8 @@ exports.createCheckoutSession = onRequest({ cors: true }, async (req, res) => {
},
],
mode: 'subscription',
success_url: `${process.env.CLIENT_URL || 'http://localhost:5174'}/#/dashboard?session_id={CHECKOUT_SESSION_ID}`,
cancel_url: `${process.env.CLIENT_URL || 'http://localhost:5174'}/#/dashboard?canceled=true`,
success_url: `${process.env.CLIENT_URL || 'http://localhost:5173'}/#/dashboard?session_id={CHECKOUT_SESSION_ID}`,
cancel_url: `${process.env.CLIENT_URL || 'http://localhost:5173'}/#/dashboard?canceled=true`,
metadata: {
orgId,
userId: authContext.userId,