# Gatsby

> Formulir Gatsby yang sudah berfungsi di HTML statis sebelum hidrasi, ditingkatkan dengan fetch setelahnya, plus Gatsby Function untuk mengirim dengan secret key.

Gatsby merender setiap halaman menjadi HTML saat build. Beri formulir `action` yang sungguhan, maka formulir mengirim sebelum React terhidrasi, atau jika bundle gagal dimuat; setelah terhidrasi, komponen mengirim dengan `fetch` dan menampilkan hasilnya di tempat.

## Kirim dari browser

### Langkah 1: Tambahkan ID formulir

`.env.production`:

```bash
GATSBY_IKIFORM_FORM_ID=frm_xxxxxxxxxxxxxxxx
```

Gatsby hanya mengekspos variabel yang diawali `GATSBY_` ke kode browser, dan menanamnya saat build. Tambahkan baris yang sama ke `.env.development` untuk `gatsby develop`.

### Langkah 2: Tambahkan komponen

`src/components/contact-form.js`:

```jsx
import * as React from "react";

const endpoint = `https://ikiform.com/f/${process.env.GATSBY_IKIFORM_FORM_ID}`;

export default function ContactForm() {
  const [status, setStatus] = React.useState("idle");
  const [message, setMessage] = React.useState("");

  async function onSubmit(event) {
    event.preventDefault();
    setStatus("sending");
    try {
      const response = await fetch(endpoint, {
        method: "POST",
        headers: { Accept: "application/json" },
        body: new FormData(event.currentTarget),
      });
      const result = await response.json();
      if (result.ok) return setStatus("sent");
      setMessage(result.error.message);
    } catch {
      setMessage("Couldn't reach the server. Try again.");
    }
    setStatus("error");
  }

  if (status === "sent") return <p>Thanks, we got it.</p>;

  return (
    <form action={endpoint} method="post" onSubmit={onSubmit}>
      <label>
        Email
        <input name="email" type="email" autoComplete="email" required />
      </label>
      <label>
        Message
        <textarea name="message" rows={5} />
      </label>
      <input type="hidden" name="_redirect" value="https://example.com/thanks/" />
      <input name="_gotcha" type="text" tabIndex={-1} autoComplete="off" hidden />
      {status === "error" ? <p role="alert">{message}</p> : null}
      <button disabled={status === "sending"}>Send</button>
    </form>
  );
}
```

Render dari halaman mana pun, misalnya `src/pages/contact.js`, dan tambahkan `src/pages/thanks.js` untuk redirect tanpa JavaScript. Untuk error field dan pemeriksaan manusia dari SDK, kembangkan seperti di [panduan React](https://ikiform.com/id/docs/guides/react.md#component).

## Kirim dari Gatsby Function

Gatsby Function bisa meneruskan kiriman dengan secret key, yang melewati domain yang diizinkan dan pemeriksaan manusia. Function hanya berjalan di host yang mendukung Gatsby Functions, seperti Netlify dengan adapter Gatsby; host statis murni seperti GitHub Pages tidak bisa menjalankannya. Simpan `IKIFORM_FORM_ID` dan secret key dengan scope `submissions:write` sebagai `IKIFORM_SECRET_KEY`, tanpa awalan `GATSBY_` agar tidak pernah sampai ke browser.

`src/api/contact.js`:

```js
export default async function handler(req, res) {
  if (req.method !== "POST") return res.status(405).end();
  const response = await fetch(`https://ikiform.com/f/${process.env.IKIFORM_FORM_ID}`, {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      Accept: "application/json",
      Authorization: `Bearer ${process.env.IKIFORM_SECRET_KEY}`,
    },
    body: JSON.stringify(req.body),
  });
  res.status(response.status).json(await response.json());
}
```

Gatsby mengurai body JSON dan form-encoded ke `req.body`, jadi halaman bisa mengirim ke `/api/contact` dengan cara mana pun. Semua kiriman lalu berbagi alamat IP function Anda untuk rate limit; lihat [Dari server](https://ikiform.com/id/docs/guides/server.md).

## Domain yang diizinkan

Ini berlaku untuk kiriman dari browser. Kiriman dari Gatsby Function memakai secret key dan melewati pemeriksaan ini.

| Tempat formulir berjalan | Tambahkan ke Domain yang diizinkan |
| --- | --- |
| `gatsby develop` dan `gatsby serve` | `localhost`. Ini cocok dengan `http://localhost:8000` dan `http://localhost:9000`: Ikiform hanya membandingkan nama host, tidak pernah port. |
| Domain Anda | `example.com`, yang juga mencakup `www.example.com` |
| Netlify | `your-site.netlify.app`. Deploy preview (`deploy-preview-12--your-site.netlify.app`) dan branch deploy (`branch--your-site.netlify.app`) bukan subdomainnya, jadi tambahkan host pratinjau persisnya selama Anda menguji. |
| Cloudflare Pages | `your-project.pages.dev`. URL pratinjau seperti `abc123.your-project.pages.dev` adalah subdomainnya, jadi sudah cocok. |

- Jangan pernah menambahkan `netlify.app` atau `pages.dev` saja. Setiap situs di host itu akan cocok, termasuk milik orang lain.

## Daftar periksa pengujian

1. Jalankan `gatsby build` dan buka `public/contact/index.html`. `action` formulir seharusnya berakhir dengan ID formulir Anda, bukan `undefined`.
2. Jalankan `gatsby serve` dengan `localhost` di domain yang diizinkan, lalu kirim. Pesan terima kasih seharusnya menggantikan formulir.
3. Matikan JavaScript, muat ulang, lalu kirim. Anda seharusnya melihat halaman terima kasih Ikiform secara lokal, atau `/thanks/` di domain `https:` Anda.
4. Jika Anda memakai function, kirim ke `/api/contact` di deploy preview dan pastikan kirimannya muncul di Ikiform.

## Pemecahan masalah

| Yang Anda lihat | Alasan | Solusi |
| --- | --- | --- |
| `404 not_found`, dan action berakhir di `/f/undefined` | Variabelnya tidak diawali `GATSBY_`, atau tidak diatur saat build | Beri nama `GATSBY_IKIFORM_FORM_ID` lalu build ulang. |
| Function menjawab `403 forbidden` | Secret key berasal dari workspace lain, sudah dicabut, atau tidak punya `submissions:write` | Buat secret key dengan scope tersebut. |
| `/api/contact` mengembalikan `404` setelah deploy | Host hanya menyajikan file statis | Deploy ke host yang mendukung Gatsby Functions, atau pakai versi browser. |
| Kiriman masuk ke filter spam dengan `origin_mismatch` | Halaman yang mengirim tidak ada di domain yang diizinkan formulir | Tambahkan host dari tabel di atas. Jika **Reject** dipilih, kiriman yang sama mendapat `403 origin_not_allowed`. |

## Terkait

- [React](https://ikiform.com/id/docs/guides/react.md): State pending, error, dan sukses.
- [Dari server](https://ikiform.com/id/docs/guides/server.md): Kirim dengan secret API key.
- [Domain yang diizinkan](https://ikiform.com/id/docs/spam.md#allowed-domains): Cara origin dicocokkan, dan apa akibat ketidakcocokan.
- [Error](https://ikiform.com/id/docs/errors.md): Setiap kode error, beserta penyebab dan solusinya.
