diff --git a/functions/index.js b/functions/index.js index 7a3c6fc..992f618 100644 --- a/functions/index.js +++ b/functions/index.js @@ -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, diff --git a/src/components/UiKit.tsx b/src/components/UiKit.tsx index 7cbe7c4..b9a63e5 100644 --- a/src/components/UiKit.tsx +++ b/src/components/UiKit.tsx @@ -99,7 +99,7 @@ const Sidebar = () => { department: inviteForm.department }); setInviteLink(result.inviteLink); - setEmailLink(result.emailLink); + // setEmailLink(result.emailLink); setInviteForm({ name: '', email: '', role: '', department: '' }); } catch (error) { console.error('Failed to invite employee:', error); @@ -126,7 +126,7 @@ const Sidebar = () => {