feat: add Cloud Functions scaffold with tested email builders

This commit is contained in:
belviskhoremk
2026-08-05 00:15:45 +00:00
parent ffd31ca1da
commit 9c47805f37
7 changed files with 4354 additions and 0 deletions

View File

@@ -0,0 +1,53 @@
import { describe, it, expect } from 'vitest'
import { buildBookingNotificationEmail, buildInquiryNotificationEmail } from './email.js'
describe('buildBookingNotificationEmail', () => {
it('includes the service, date, time, and contact details', () => {
const { subject, html } = buildBookingNotificationEmail({
serviceName: 'Knotless Braids',
date: '2026-09-01',
time: '10:00 AM',
contact: { name: 'Jane Doe', phone: '555-0100', email: 'jane@example.com', notes: 'First time client' },
})
expect(subject).toBe('New booking: Knotless Braids on 2026-09-01')
expect(html).toContain('Jane Doe')
expect(html).toContain('Knotless Braids')
expect(html).toContain('2026-09-01')
expect(html).toContain('10:00 AM')
expect(html).toContain('555-0100')
expect(html).toContain('jane@example.com')
expect(html).toContain('First time client')
})
it('omits the notes paragraph when notes are empty', () => {
const { html } = buildBookingNotificationEmail({
serviceName: 'Cornrows',
date: '2026-09-02',
time: '1:00 PM',
contact: { name: 'Amy', phone: '555-0101', email: 'amy@example.com', notes: '' },
})
expect(html).not.toContain('Notes:')
})
})
describe('buildInquiryNotificationEmail', () => {
it('includes the product and contact details', () => {
const { subject, html } = buildInquiryNotificationEmail({
productName: 'Silk Edge Control',
contact: { name: 'Nadia', email: 'nadia@example.com', notes: 'Is this in stock?' },
})
expect(subject).toBe('New inquiry: Silk Edge Control')
expect(html).toContain('Nadia')
expect(html).toContain('nadia@example.com')
expect(html).toContain('Silk Edge Control')
expect(html).toContain('Is this in stock?')
})
it('omits the notes paragraph when notes are empty', () => {
const { html } = buildInquiryNotificationEmail({
productName: 'Braid Oil',
contact: { name: 'Kim', email: 'kim@example.com', notes: '' },
})
expect(html).not.toContain('Notes:')
})
})

28
functions/src/email.ts Normal file
View File

@@ -0,0 +1,28 @@
export interface BookingEmailInput {
serviceName: string
date: string
time: string
contact: { name: string; phone: string; email: string; notes: string }
}
export function buildBookingNotificationEmail(input: BookingEmailInput): { subject: string; html: string } {
const notesLine = input.contact.notes ? `<p>Notes: ${input.contact.notes}</p>` : ''
return {
subject: `New booking: ${input.serviceName} on ${input.date}`,
html: `<p><strong>${input.contact.name}</strong> booked <strong>${input.serviceName}</strong> on ${input.date} at ${input.time}.</p>` +
`<p>Phone: ${input.contact.phone}<br/>Email: ${input.contact.email}</p>${notesLine}`,
}
}
export interface InquiryEmailInput {
productName: string
contact: { name: string; email: string; notes: string }
}
export function buildInquiryNotificationEmail(input: InquiryEmailInput): { subject: string; html: string } {
const notesLine = input.contact.notes ? `<p>Notes: ${input.contact.notes}</p>` : ''
return {
subject: `New inquiry: ${input.productName}`,
html: `<p><strong>${input.contact.name}</strong> (${input.contact.email}) asked about <strong>${input.productName}</strong>.</p>${notesLine}`,
}
}