🇲🇦Morocco @zrigou

Account created on 12 June 2024, 9 months ago
#

Recent comments

🇲🇦Morocco zrigou

I am currently working on a feature that involves submitting data to Drupal using the FormData format, particularly for forms that include file uploads. However, I've noticed that while data sent as JSON is processed correctly, the data sent using FormData is not being accepted by Drupal.

Could you please advise if there are any specific configurations or steps required on the Drupal side to ensure that FormData submissions are handled correctly? I want to ensure that I'm following best practices for handling such data submissions.

import { GetStaticPaths, GetStaticProps } from "next";
import { DrupalClient, DrupalNode } from "next-drupal";
import { useState, FormEvent } from "react";
import { useTranslation } from "next-i18next";
import { useRouter } from "next/router";

import { Body } from "@/components/body";
import { HeadingPage } from "@/components/heading--page";
import { Meta } from "@/components/meta";
import {
  CommonPageProps,
  getCommonPageProps,
} from "@/lib/get-common-page-props";

import { env } from "@/env";

interface MetaTag {
  tag: string;
  attributes: {
    name?: string;
    content?: string;
    rel?: string;
    href?: string;
  };
}

interface careersProps extends CommonPageProps {
  career: DrupalNode;
}

export default function careers({ career }: careersProps) {
    console.log('career' , career);

  const { t } = useTranslation();
  const [submissionStatus, setSubmissionStatus] = useState<"success" | "error" | null>(null);
  const router = useRouter();

  const imageLink = (career.metatag as MetaTag[] | undefined)?.find(
    (meta: MetaTag) =>
      meta.tag === "link" && meta.attributes.rel === "image_src",
  )?.attributes.href;

  async function handleSubmit(event: FormEvent<HTMLFormElement>) {
    event.preventDefault();
    
    const formData = new FormData(event.currentTarget);
    formData.append('webform_id', 'careers');
    formData.append('entity_id', career.id);

    try {
      const response = await fetch(
        `${env.NEXT_PUBLIC_DRUPAL_BASE_URL}/webform_rest/submit`,
        {
          method: "POST",
          body: formData,
        }
      );

      if (response.ok) {
        setSubmissionStatus("success");
        setTimeout(() => router.push('/careers'), 3000);
        console.log('Submission successful:', await response.json());
      } else {
        setSubmissionStatus("error");
        console.error('Submission error:', response.statusText);
      }
    } catch (error) {
      setSubmissionStatus("error");
      console.error('Submission failed:', error);
    }
  }

  return (
    <>
      <Meta title={career.title} metatags={[]} />
      <HeadingPage>{career.title}</HeadingPage>
      <div className="mt-8 space-y-6">
        {imageLink && (
          <div className="overflow-hidden rounded-lg shadow-lg">
            <img
              src={imageLink}
              alt={career.title}
              className="w-full h-auto object-cover"
            />
          </div>
        )}
        <div className="prose max-w-none">
          <Body value={career.body.processed} />
        </div>
        {submissionStatus === "success" ? (
          <div className="p-4 text-green-700 bg-green-100 border border-green-700 rounded">
            {t("Your application has been submitted successfully.")}
          </div>
        ) : submissionStatus === "error" ? (
          <div className="p-4 text-red-700 bg-red-100 border border-red-700 rounded">
            {t("There was an error submitting your application. Please try again.")}
          </div>
        ) : (
          <form onSubmit={handleSubmit} className="space-y-6">
            <div>
              <label className="block text-sm font-medium text-gray-700" htmlFor="subject">{t("Subject")}</label>
              <input
                type="text"
                id="subject"
                name="subject"
                required
                className="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm"
              />
            </div>
            <div>
              <label className="block text-sm font-medium text-gray-700" htmlFor="firstName">{t("First Name")}</label>
              <input
                type="text"
                id="firstName"
                name="firstName"
                required
                className="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm"
              />
            </div>
            <div>
              <label className="block text-sm font-medium text-gray-700" htmlFor="lastName">{t("Last Name")}</label>
              <input
                type="text"
                id="lastName"
                name="lastName"
                required
                className="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm"
              />
            </div>
            <div>
              <label className="block text-sm font-medium text-gray-700" htmlFor="email">{t("Email")}</label>
              <input
                type="email"
                id="email"
                name="email"
                required
                className="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm"
              />
            </div>
            <div>
              <label className="block text-sm font-medium text-gray-700" htmlFor="note">{t("Note")}</label>
              <textarea
                id="note"
                name="note"
                required
                className="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm"
              ></textarea>
            </div>
            <div>
              <label className="block text-sm font-medium text-gray-700" htmlFor="cv">{t("CV")}</label>
              <input
                type="file"
                id="cv"
                name="cv"
                required
                className="mt-1 block w-full text-sm text-gray-500 file:mr-4 file:py-2 file:px-4 file:rounded-md file:border-0 file:text-sm file:font-semibold file:bg-indigo-50 file:text-indigo-700 hover:file:bg-indigo-100"
              />
            </div>
            <button
              type="submit"
            //   className="w-full inline-flex justify-center py-2 px-4 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500"
            >
              {t("Submit")}
            </button>
          </form>
        )}
      </div>
    </>
  );
}

export const getStaticPaths: GetStaticPaths = async () => {
  const drupalClient = new DrupalClient(env.NEXT_PUBLIC_DRUPAL_BASE_URL, {
    auth: {
      clientId: env.DRUPAL_CLIENT_ID,
      clientSecret: env.DRUPAL_CLIENT_SECRET,
    },
  });

  const careersResponse = await drupalClient.getResourceCollection<DrupalNode[]>(
    "node--career_position",
    {
      params: {
        "fields[node--career_position]": "id",
        "page[limit]": 50,
      },
    },
  );

  const paths = careersResponse.map((career) => ({
    params: { id: career.id },
  }));

  return {
    paths,
    fallback: "blocking",
  };
};

export const getStaticProps: GetStaticProps<careersProps> = async (
  context,
) => {
  const { id } = context.params as { id: string };
  const drupalClient = new DrupalClient(env.NEXT_PUBLIC_DRUPAL_BASE_URL, {
    auth: {
      clientId: env.DRUPAL_CLIENT_ID,
      clientSecret: env.DRUPAL_CLIENT_SECRET,
    },
  });

  try {
    const career = await drupalClient.getResource<DrupalNode>(
      "node--career_position",
      id,
    );

    return {
      props: {
        ...(await getCommonPageProps(context)),
        career,
      },
      revalidate: 60,
    };
  } catch (error) {
    console.error("Error fetching career post:", error);
    return {
      notFound: true,
    };
  }
};
Production build 0.71.5 2024