diff --git a/src/routes/contact-us/+page.server.ts b/src/routes/contact-us/+page.server.ts index 3788b552..f859f2db 100644 --- a/src/routes/contact-us/+page.server.ts +++ b/src/routes/contact-us/+page.server.ts @@ -19,6 +19,7 @@ async function sendContactEmail(data: { message: string type: 'Standard' | 'Media' | 'Partnerships' | 'Feedback' organization?: string + city_country?: string }) { if (!env.MAILERSEND_API_KEY) { console.error('MAILERSEND_API_KEY is not configured') @@ -40,9 +41,13 @@ async function sendContactEmail(data: { htmlContent += `

Organization: ${data.organization}

` } + if (data.city_country) { + htmlContent += `

City, Country: ${data.city_country}

` + } + htmlContent += `

Message:

${data.message.replace(/\n/g, '
')}

` - const textContent = `Name: ${data.name}\nEmail: ${data.email}\nSubject: ${data.subject}${data.organization ? `\nOrganization: ${data.organization}` : ''}\n\nMessage:\n${data.message}` + const textContent = `Name: ${data.name}\nEmail: ${data.email}\nSubject: ${data.subject}${data.organization ? `\nOrganization: ${data.organization}` : ''}${data.city_country ? `\nCity, Country: ${data.city_country}` : ''}\n\nMessage:\n${data.message}` // Build the request body for MailerSend API const emailBody: { @@ -55,7 +60,7 @@ async function sendContactEmail(data: { } = { from: { email: 'info@pauseai.info', - name: 'PauseAI Contact Form' + name: `PauseAI ${data.type} Form` }, to: [ { @@ -63,7 +68,7 @@ async function sendContactEmail(data: { name: 'PauseAI Team' } ], - subject: `[Contact Form] ${data.subject}`, + subject: `[${data.type} Form]: ${data.subject}`, html: htmlContent, text: textContent } @@ -225,10 +230,18 @@ export const actions: Actions = { const name = data.get('name')?.toString() const email = data.get('email')?.toString() const organization = data.get('organization')?.toString() - const subject = data.get('subject')?.toString() + const city_country = data.get('city_country')?.toString() + + let subject = data.get('partnership_type')?.toString() || '' + const other_type = data.get('other_partnership_type')?.toString() + + if (subject === 'Other' && other_type) { + subject = `Other: ${other_type}` + } + const message = data.get('message')?.toString() - if (!name || !email || !organization || !subject || !message) { + if (!name || !email || !city_country || !subject || !message) { return fail(400, { message: 'Missing required fields' }) } @@ -236,6 +249,7 @@ export const actions: Actions = { name, email, organization, + city_country, subject, message, type: 'Partnerships' diff --git a/src/routes/contact-us/+page.svelte b/src/routes/contact-us/+page.svelte index df0f768c..ea34316e 100644 --- a/src/routes/contact-us/+page.svelte +++ b/src/routes/contact-us/+page.svelte @@ -15,10 +15,64 @@ let formData = { media: { name: '', email: '', subject: '', organization: '', details: '' }, - partnerships: { name: '', email: '', organization: '', subject: '', message: '' }, + partnerships: { + name: '', + email: '', + organization: '', + city_country: '', + partnership_type: '', + other_partnership_type: '', + message: '' + }, feedback: { name: '', email: '', subject: '', message: '' } } + const partnershipOptions = [ + 'Mobilize grassroots support for PauseAI’s mission', + 'Open a chapter in my city/country', + 'Organize a public demonstration or protest', + 'Support citizen lobbying efforts', + 'Invite Pause AI to participate/speak at your local event/meeting', + 'Test and refine policy proposals with policymakers', + 'Amplify Pause AI campaign and/or proposal', + 'Assist with surveys or qualitative data collection', + 'Help disseminate research findings to the public', + 'Connect Pause AI with experts', + 'Share grassroots public sentiment data', + 'Exchange volunteers for events or campaigns', + 'Pool resources for joint campaigns or event', + 'Collaborate on grant applications', + 'Co-create educational or advocacy content', + 'Mobilize volunteers for emergency response', + 'Adapt messaging for local/international contexts', + 'Connect with engaged volunteer base', + 'Explore general partnership opportunities', + 'Other' + ] + + function countWords(str: string) { + return str.trim().split(/\s+/).length + } + + function validatePartnershipForm() { + if (activeTab !== 'partnerships') return true + + if ( + formData.partnerships.partnership_type === 'Other' && + countWords(formData.partnerships.other_partnership_type) > 10 + ) { + toast.error('Other partnership type must be 10 words or less.') + return false + } + + const messageWords = countWords(formData.partnerships.message) + if (messageWords > 200) { + toast.error(`Message must be 200 words or less. (Current: ${messageWords} words)`) + return false + } + return true + } + onMount(() => { const tab = $page.url.searchParams.get('tab') if (tab === 'media') { @@ -45,7 +99,19 @@ localStorage.setItem('contactFormData', JSON.stringify(formData)) } - function handleEnhance() { + function handleEnhance({ cancel }: { cancel: () => void }) { + if (activeTab === 'partnerships') { + // Manually validate because we can't easily use the form state inside the enhancer + // without potentially stale data if we just used `formData` variable. + // But `formData` variable IS bound to inputs, so it should be fine. + // However, `data` from the argument contains the actual FormData being submitted. + + if (!validatePartnershipForm()) { + cancel() + return + } + } + loading = true return async ({ result, @@ -66,7 +132,9 @@ name: '', email: '', organization: '', - subject: '', + city_country: '', + partnership_type: '', + other_partnership_type: '', message: '' } } else if (activeTab === 'feedback') { @@ -119,9 +187,8 @@ {#if activeTab === 'partnerships'}

- Interested in collaborating? Read about our partnership opportunities. + Ready to collaborate with PauseAI's network to build the momentum required to drive + meaningful change in AI policy? We would love to hear from you.

@@ -149,27 +216,57 @@ type="text" id="part-org" name="organization" - required - placeholder="Organization" + placeholder="Organization (Optional)" bind:value={formData.partnerships.organization} />
+ + +
+ + {#if formData.partnerships.partnership_type === 'Other'} +
+ +
+ {/if} + +
+
@@ -456,4 +553,37 @@ gap: 0.5rem; } } + + select { + width: 100%; + padding: 0.8rem 1.2rem; + border: 1px solid var(--brand-subtle); + border-radius: 20px; + background-color: var(--bg); + color: var(--text); + font-family: var(--font-body); + font-size: 1rem; + font-weight: 300; + box-sizing: border-box; + display: block; + appearance: none; + background-image: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3e%3cpolyline points='6 9 12 15 18 9'%3e%3c/polyline%3e%3c/svg%3e"); + background-repeat: no-repeat; + background-position: right 1rem center; + background-size: 1em; + } + + select:focus { + outline: 2px solid var(--brand); + border-color: transparent; + } + + .field-label { + margin-bottom: 0.5rem; + margin-left: 0.5rem; + font-size: 0.9rem; + font-weight: 500; + color: var(--text); + opacity: 0.9; + }