Skip to content

contact form and netlify function#64

Open
interim17 wants to merge 16 commits intotrunk/emailfrom
feature/contact
Open

contact form and netlify function#64
interim17 wants to merge 16 commits intotrunk/emailfrom
feature/contact

Conversation

@interim17
Copy link
Contributor

@interim17 interim17 commented Mar 3, 2026

Problem

Advances #44
Closes #60 and #61

Solution

Building out the initial scaffolding for our email/contact system.

In terms of design I think this a placeholder, aiming for a functional proof of concept.

In this PR:

  • added "trunk/**" to pull request branches in ci.yml so we can get checks on feature trunks (TBD if it works)
  • removed (commented out) start and comment icons as we are not implementing those yet
  • each idea page renders a contact form modal
  • the modal takes in user submissions and triggers a netlify function contact
  • the response from the function is logged if succesful

This is a fairly harmless template that we can build on top of as we decide what email tooling we want to use and how we want to store/manage email addresses.

Testing this function locally with netlify cli seems to work, I'm not sure if it will deployed in preview or only when we merge, watching this as it moves through PR will be a useful scan of that system.

Screenshot 2026-03-03 at 3 49 57 PM Screenshot 2026-03-03 at 3 46 29 PM

@netlify
Copy link

netlify bot commented Mar 3, 2026

Deploy Preview for project-idea-board ready!

Name Link
🔨 Latest commit 4094e4d
🔍 Latest deploy log https://app.netlify.com/projects/project-idea-board/deploys/69a774e763e3fd0008555497
😎 Deploy Preview https://deploy-preview-64--project-idea-board.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@interim17 interim17 changed the base branch from main to trunk/email March 3, 2026 23:57
@interim17 interim17 requested a review from Copilot March 3, 2026 23:59
@interim17 interim17 changed the title Feature/contact contact form and netlify function Mar 3, 2026
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Initial scaffolding for an idea-page “Contact” flow, including a UI modal and a Netlify Function endpoint, plus CMS/schema updates to support a designated primary contact.

Changes:

  • Add ContactModal and wire it into the idea post template to submit to /.netlify/functions/contact.
  • Add primaryContact to Decap CMS config + Gatsby schema/query/frontmatter.
  • Add Netlify Functions dependency and ignore local Netlify state folder.

Reviewed changes

Copilot reviewed 8 out of 10 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
package.json Adds @netlify/functions dependency for the new serverless handler.
yarn.lock Locks new Netlify Functions dependency (+ types).
netlify/functions/contact.mts Introduces the contact Netlify Function (currently echo-style response).
src/components/ContactModal.tsx New modal UI that collects sender info and POSTs to the Netlify function.
src/templates/idea-post.tsx Adds “Contact” button/modal to idea pages and queries authors + primaryContact.
gatsby/schema/base.gql Extends Frontmatter with primaryContact.
static/admin/config.yml Adds primaryContact relation field to the CMS idea editor.
src/pages/ideas/dev-example.md Adds primaryContact (and reorganizes some frontmatter content) in an example idea.
.gitignore Ignores local .netlify folder.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

You can also share your feedback on Copilot code review. Take the survey.

Comment on lines +5 to +16
if (event.body === null) {
return {
statusCode: 400,
body: JSON.stringify("Payload required"),
};
}

const requestBody = JSON.parse(event.body) as {
senderName: string;
senderEmail: string;
recipient: string;
message: string;

This comment was marked as resolved.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

out of scope

Comment on lines +19 to +22
return {
statusCode: 200,
body: JSON.stringify(requestBody),
};

This comment was marked as resolved.

Comment on lines 6 to 15

import { ContactModal } from "../components/ContactModal";
import IconText from "../components/IconText";
import Layout from "../components/Layout";

This comment was marked as resolved.

Comment on lines +34 to +41
fetch("/.netlify/functions/contact", {
method: "POST",
body: JSON.stringify({
senderName: senderName,
senderEmail: senderEmail,
recipient: recipientLabel,
message: message,
}),

This comment was marked as resolved.

@interim17 interim17 marked this pull request as ready for review March 4, 2026 17:30
Copy link

@ShrimpCryptid ShrimpCryptid left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Left a bunch of questions, overall looks good! Re-request me when you want me to take another look :>

};
};

export { handler }; No newline at end of file

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Missing whitespace at EOF-- can Prettier handle these file types?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, yeah maybe missing that extension

const recipientLabel = hasPrimaryContact
? primaryContact
: hasAuthors
? (authors?.filter(Boolean).join(", ") ?? "the authors")

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the nullish coalescing operator necessary here? Shouldn't authors be defined?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So in this repo there will be a hardening step that will ensure this is defined, but that will come in a subsequent PR.

Currently authors is inferred and not an explicit part of our schema. We will add a more structured type and resolver to the gatsby node APIs when we know what we need. We will likely query both a name and a contactId to use in getting an environment variable (email address stored as secret).

For now gatsby is just inferring that its (string | nuull) | null

? primaryContact
: hasAuthors
? (authors?.filter(Boolean).join(", ") ?? "the authors")
: "fake default email inbox";

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add a TODO or something so this doesn't get left in the code in the future? 🤭

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will do, but there is kind of a TODO on all of this code, since we don't have a design and we don't know the shape of the data/query yet.


const handleSubmit = async () => {
try {
const response = await fetch("/.netlify/functions/contact", {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should these function paths be stored somewhere as constants? What validates that these exist?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm going ahead and storing these now as constants which is good feedback, this PR was the first time we are defining and calling netlify functions.

By default in netlify the file name for the function defined in netlify/functions is the path.

A constant doesn't hurt, but I don't currently know how I plan to do more robust validation, especially given that not everyone has access to the netlify CLI for this repo, currently just me and Megan, so we don't have a good set up for testing and verifying across users and reviewers.

I think best thing will be to do it in CI/CD via the netlify back end but I have to look into it a bit.

console.log("Failed to send message. Please try again later.");
}
} catch (error) {
console.error("Error sending message:", error);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be shown on the modal as error text or something?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably something like that for real failure points down the line

Comment on lines +48 to +50
if (response.ok) {
console.log("Message sent successfully, response:", response);
} else {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should the modal close itself on a success and show an error message on failure?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good question

<p>
<strong>Idea:</strong> {title}
</p>
{authors && authors.length > 0 && (

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use hasAuthors from above?

{authors && authors.length > 0 && (
<p>
<strong>Authors:</strong>{" "}
{authors.filter(Boolean).join(", ")}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Repeated filtering here, could do it once up above

Comment on lines +8 to +9
// MessageOutlined,
// StarOutlined,

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove? I feel like it's better just to reimport these if the related comment block is uncommented in the future rather than leave these hanging out.

@interim17 interim17 requested a review from ShrimpCryptid March 5, 2026 00:28
@interim17
Copy link
Contributor Author

@ShrimpCryptid thanks for review. I pushed some changed in response to your feedback and also deferred on a couple things since this is still somewhat of a draft to just start setting up the use of netlify functions. Note that we aren't merging to main!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

idea pages contact button/form

3 participants