联系表单无法使用 NextJS 和 Nodemailer

Contact form not working using NextJS and Nodemailer

提问人: 提问时间:11/7/2023 更新时间:11/7/2023 访问量:17

问:

这是我的 ContactForm.tsx:

"use client";
import { useFormik } from "formik";
import * as Yup from "yup";
import React, { useState } from "react";
import SuccessModal from "@/components/SuccessModal";

interface FormValues {
  name: string;
  organisation: string;
  email: string;
  location: string;
  number: string;
  message: string;
  design: boolean;
  maintenance: boolean;
  repairs: boolean;
}

const ContactForm: React.FC = () => {
  const [isModalOpen, setIsModalOpen] = useState(false);
  const [isResetting, setIsResetting] = useState(false);

  const openModal = () => {
    setIsModalOpen(true);
  };

  const closeModal = () => {
    setIsModalOpen(false);
    setIsResetting(true);

    setTimeout(() => {
      setIsResetting(false);
      formik.resetForm();
    }, 500);
  };

  const formik = useFormik<FormValues>({
    initialValues: {
      name: "",
      organisation: "",
      email: "",
      location: "south-australia",
      number: "",
      message: "",
      design: false,
      maintenance: false,
      repairs: false,
    },

    validationSchema: Yup.object().shape({
      name: Yup.string().required("Full Name is required"),
      organisation: Yup.string().required("School / Organisation is required"),
      email: Yup.string()
        .email("Invalid email address")
        .required("Email is required"),
      location: Yup.string().required("Location is required"),
      number: Yup.string().required("Phone number is required"),
      message: Yup.string().required("Message is required"),
      design: Yup.boolean(),
      maintenance: Yup.boolean(),
      repairs: Yup.boolean(),
    }),

    onSubmit: async (values) => {
      try {
        // Make a POST request to the server-side API route
        const response = await fetch("/api/sendEmail", {
          method: "POST",
          body: JSON.stringify(values),
          headers: {
            "Content-Type": "application/json",
          },
        });

        if (response.ok) {
          console.log("Form values submitted:", values);
          openModal();
        } else {
          console.error("Error sending email");
          // Handle the error condition as needed
        }
      } catch (error) {
        console.error("Error sending email:", error);
      }
    },
  });

  return (
    <div
      id="contact"
      className="container my-20 px-5 grid gap-8 grid-cols-1 md:grid-cols-2 py-24 mx-auto bg-gray-100 text-gray-900 rounded-lg"
    >
      <div className="flex flex-col justify-center">
        <div>
          <h2 className="text-4xl lg:text-5xl font-bold leading-tight text-[#a4c4b5]">
            Let&apos;s make some plans!
          </h2>
          <div className="text-gray-700 mt-8">
            Want to send us an email? <br />
            <a
              className="underline text-sm"
              href="mailto:[email protected]"
            >
              Click Here
            </a>
          </div>
        </div>
        <div className="mt-12 text-center ml-7 md:ml-0">
          <img
            src={"./assets/contact.jpg" || "https://dummyimage.com/720x600"}
            alt="Contact"
            className="w-[90%] rounded-xl"
          />
        </div>
      </div>
      <form
        className={`${
          isResetting
            ? "animate-fadeOut" // Apply fade-out animation class when resetting
            : "animate-fadeIn" // Apply fade-in animation class when displaying
        } mt-8`}
        onSubmit={formik.handleSubmit}
      >
        <div className="mt-8">
          <label
            htmlFor="name"
            className="uppercase text-sm text-gray-600 font-bold"
          >
            Full Name
          </label>
          <input
            autoComplete="off"
            type="text"
            id="name"
            name="name"
            value={formik.values.name}
            onChange={formik.handleChange}
            onBlur={formik.handleBlur}
            placeholder="Enter your name"
            className={`w-full bg-gray-200 text-gray-900 mt-2 p-3 border-2 rounded-lg focus:outline-none focus:ring-2 focus:ring-[#a4c4b5] ${
              formik.touched.name && formik.errors.name
                ? "border-red-500" // Add error border
                : ""
            }`}
            required
          />
          {formik.touched.name && formik.errors.name && (
            <div className="text-red-500 text-sm mt-1">
              {formik.errors.name}
            </div>
          )}
        </div>
        <div className="mt-8">
          <label
            htmlFor="organisation"
            className="uppercase text-sm text-gray-600 font-bold"
          >
            School / Organisation
          </label>
          <input
            autoComplete="off"
            type="text"
            id="organisation"
            name="organisation"
            value={formik.values.organisation}
            onChange={formik.handleChange}
            onBlur={formik.handleBlur}
            placeholder="Enter details"
            className={`w-full bg-gray-200 border-2 text-gray-900 mt-2 p-3 rounded-lg focus:outline-none focus:ring-2 focus:ring-[#a4c4b5] ${
              formik.touched.organisation && formik.errors.organisation
                ? "border-red-500" // Add error border
                : ""
            }`}
            required
          />
          {formik.touched.organisation && formik.errors.organisation && (
            <div className="text-red-500 text-sm mt-1">
              {formik.errors.organisation}
            </div>
          )}
        </div>
        <div className="mt-8 flex flex-col">
          <label
            htmlFor="location"
            className="uppercase text-sm text-gray-600 font-bold"
          >
            Location
          </label>
          <select
            id="location"
            name="location"
            value={formik.values.location}
            onChange={formik.handleChange}
            className={`w-full bg-gray-200 border-2 text-gray-900 mt-2 p-3 rounded-lg focus:outline-none focus:ring-2 focus:ring-[#a4c4b5] ${
              formik.touched.location && formik.errors.location
                ? "border-red-500" // Add error border
                : ""
            }`}
          >
            <option value="south-australia">South Australia</option>
            <option value="queensland">Queensland</option>
            <option value="western-australia">Western Australia</option>
          </select>
          {formik.touched.location && formik.errors.location && (
            <div className="text-red-500 text-sm mt-1">
              {formik.errors.location}
            </div>
          )}
        </div>
        <div className="mt-8">
          <label
            htmlFor="email"
            className="uppercase text-sm text-gray-600 font-bold"
          >
            Email
          </label>
          <input
            autoComplete="off"
            type="email"
            id="email"
            name="email"
            value={formik.values.email}
            onChange={formik.handleChange}
            onBlur={formik.handleBlur}
            placeholder="Enter your email address"
            className={`w-full bg-gray-200 border-2 text-gray-900 mt-2 p-3 rounded-lg focus:outline-none focus:ring-2 focus:ring-[#a4c4b5] ${
              formik.touched.email && formik.errors.email
                ? "border-red-500" // Add error border
                : ""
            }`}
            required
          />
          {formik.touched.email && formik.errors.email && (
            <div className="text-red-500 text-sm mt-1">
              {formik.errors.email}
            </div>
          )}
        </div>
        <div className="mt-8">
          <label
            htmlFor="number"
            className="uppercase text-sm text-gray-600 font-bold"
          >
            Phone Number
          </label>
          <input
            autoComplete="off"
            type="tel"
            id="number"
            name="number"
            value={formik.values.number}
            onChange={formik.handleChange}
            onBlur={formik.handleBlur}
            placeholder="Enter your best contact number"
            className={`w-full bg-gray-200 border-2 text-gray-900 mt-2 p-3 rounded-lg focus:outline-none focus:ring-2 focus:ring-[#a4c4b5] ${
              formik.touched.number && formik.errors.number
                ? "border-red-500" // Add error border
                : ""
            }`}
            required
          />
          {formik.touched.number && formik.errors.number && (
            <div className="text-red-500 text-sm mt-1">
              {formik.errors.number}
            </div>
          )}
        </div>
        <div className="mt-8">
          <label className="uppercase text-sm text-gray-600 font-bold">
            Select Relevant:
          </label>
          <div className="mt-2">
            <input
              autoComplete="off"
              type="checkbox"
              id="design"
              name="design"
              checked={formik.values.design}
              onChange={formik.handleChange}
              className="mr-2 text-gray-600 font-bold"
            />
            <label htmlFor="design">Design + Build</label>
          </div>
          <div>
            <input
              autoComplete="off"
              type="checkbox"
              id="maintenance"
              name="maintenance"
              checked={formik.values.maintenance}
              onChange={formik.handleChange}
              className="mr-2"
            />
            <label htmlFor="maintenance">Maintenance</label>
          </div>
          <div>
            <input
              autoComplete="off"
              type="checkbox"
              id="repairs"
              name="repairs"
              checked={formik.values.repairs}
              onChange={formik.handleChange}
              className="mr-2"
            />
            <label htmlFor="repairs">Repairs</label>
          </div>
        </div>
        <div className="mt-8">
          <label
            htmlFor="message"
            className="uppercase text-sm text-gray-600 font-bold"
          >
            Message
          </label>
          <textarea
            id="message"
            name="message"
            value={formik.values.message}
            onChange={formik.handleChange}
            onBlur={formik.handleBlur}
            placeholder="Enter your message - include as much information as possible"
            className={`w-full h-32 bg-gray-200 border-2 text-gray-900 mt-2 p-3 rounded-lg focus:outline-none focus:ring-2 focus:ring-[#a4c4b5] ${
              formik.touched.message && formik.errors.message
                ? "border-red-500" // Add error border
                : ""
            }`}
            required
          ></textarea>
          {formik.touched.message && formik.errors.message && (
            <div className="text-red-500 text-sm mt-1">
              {formik.errors.message}
            </div>
          )}
        </div>
        <div className="mt-8">
          <button
            type="submit"
            className="uppercase text-sm font-bold tracking-wide bg-[#a4c4b5] text-gray-100 p-3 rounded-lg w-full focus:outline-none focus:ring-2 focus:ring-[#a4c4b5] hover:bg-[#94b4a5]"
            disabled={formik.isSubmitting}
          >
            Send Message
          </button>
        </div>
      </form>
      <SuccessModal isOpen={isModalOpen} onClose={closeModal} />
    </div>
  );
};

export default ContactForm;

这是我的sendEmail.ts:

import nodemailer from "nodemailer";

interface FormData {
  name: string;
  organisation: string;
  email: string;
  location: string;
  number: string;
  message: string;
  design: boolean;
  maintenance: boolean;
  repairs: boolean;
}

export async function sendMail(formData: FormData) {
  try {
    const transporter = nodemailer.createTransport({
      service: "gmail",
      auth: {
        user: process.env.USER,
        pass: process.env.PASS,
      },
    });

    const mailOptions = {
      from: `"${formData.name}" <${formData.email}>`,
      to: "[email protected]", // Replace with the recipient's email address
      subject: "Contact Form Submission",
      html: `
        <h2>Contact Form Submission</h2>
        <p><strong>Name:</strong> ${formData.name}</p>
        <p><strong>Organisation:</strong> ${formData.organisation}</p>
        <p><strong>Location:</strong> ${formData.location}</p>
        <p><strong>Phone Number:</strong> ${formData.number}</p>
        <p><strong>Message:</strong> ${formData.message}</p>
        <p><strong>Design:</strong> ${formData.design ? "Yes" : "No"}</p>
        <p><strong>Maintenance:</strong> ${
          formData.maintenance ? "Yes" : "No"
        }</p>
        <p><strong>Repairs:</strong> ${formData.repairs ? "Yes" : "No"}</p>
      `,
    };

    const info = await transporter.sendMail(mailOptions);
    console.log("Email Sent");
    return true;
  } catch (error) {
    console.error("Error sending email:", error);
    throw error;
  }
}

这是我的文件夹结构:

- playground_guardians
    - app
        - .env.local
        - favicon.ico
        - globals.css
        - layout.tsx
        - page.tsx
    - components
        - Containers
            - AboutContainer.tsx
            - CardContainer.tsx
            - FeaturedContainer.tsx
        - About.tsx
        - CardFeature.tsx
        - CompanyName.tsx
        - ContactForm.tsx
        - Feature.tsx
        - Footer.tsx
        - Hero.tsx
        - Mission.tsx
        - Navbar.tsx
        - SuccessModal.tsx
    - node_modules
    -pages
        - api
            - sendEmail.ts
    - public
    - .eslintrc.json
    - .gitignore
    - next-env.d.ts
    - next.config.js
    - package-lock.json
    - package.json
    - postcss.config.json
    - README.md
    - tailwind.configt.ts
    - tsconfig.json

这是我第一次创建发送到电子邮件的联系表单,我在创建此类组件方面没有太多经验。

我收到 404 错误,但现在我收到此错误:

ContactForm.tsx:67 POST http://localhost:3000/api/sendEmail 500(内部服务器错误)

onSubmit @ ContactForm.tsx:67

eval @ formik.esm.js:956

eval @ formik.esm.js:1266

eval @ formik.esm.js:872

Promise.then(异步)

eval @ formik.esm.js:848

eval @ formik.esm.js:1266

eval @ formik.esm.js:936

eval @ formik.esm.js:1266

callcallback @ react-dom.development.js:20367

invokeGuardedCallbackImpl @ react-dom.development.js:20416

invokeGuardedCallback @ react-dom.development.js:20491

invokeGuardedCallbackAndCatchFirstError @ react-dom.development.js:20505

executeDispatch @ react-dom.development.js:31842

进程调度队列项目顺序 @ react-dom.development.js:31874

进程调度队列 @ react-dom.development.js:31887

dispatchEventsForPlugins @ react-dom.development.js:31898

eval @ react-dom.development.js:32088

批处理更新$1 @ react-dom.development.js:24699

批处理更新 @ react-dom.development.js:28559

dispatchEventForPluginEventSystem @ react-dom.development.js:32087

调度事件 @ react-dom.development.js:29855

调度离散事件 @ react-dom.development.js:29826

app-index.js:31 发送电子邮件时出错

请帮忙

下一个.js 节点邮件

评论

0赞 Community 11/7/2023
请修剪您的代码,以便更轻松地找到您的问题。请遵循这些准则,以创建最小的可重现示例

答: 暂无答案