Script From Registrasi dan Login Full AuthFormWithDrive.jsx

admin

 Formulir

Script From Registrasi dan Login Full AuthFormWithDrive.jsx



import React, { useState, useEffect, useRef } from "react";


/**

 * AuthFormWithDrive.jsx

 * Single-file React component (Tailwind) that provides:

 * - Normal (inline) registration & login forms

 * - Modal (popup) registration & login forms

 * - Fields: name, address, phone, email, password

 * - Social login buttons (Google / Facebook / GitHub) with example integration points

 * - Save form data to Google Drive (via Google Drive REST API / gapi / Google Identity Services)

 *

 * IMPORTANT SETUP NOTES (read + follow):

 * 1) Tailwind: this component assumes your project is set up with Tailwind CSS.

 * 2) Google APIs: create OAuth credentials in Google Cloud Console

 *    - Enable Drive API and/or use Google Identity Services (GSI) for sign-in.

 *    - Obtain CLIENT_ID and (optionally) API_KEY if using gapi.

 *    - For Drive uploads from client, you need an OAuth access token with scope: https://www.googleapis.com/auth/drive.file

 *    - For production, it's recommended to perform OAuth on the server to keep client secrets safe.

 * 3) Facebook/GitHub: implement OAuth on your backend. Social buttons here call /auth/:provider endpoints as examples.

 * 4) This component focuses on UI + client integration points. Replace placeholders with your backend endpoints and keys.

 *

 * Usage: import default export and render in your app.

 */


const GOOGLE_CLIENT_ID = "YOUR_GOOGLE_CLIENT_ID.apps.googleusercontent.com"; // replace

const GOOGLE_API_KEY = "YOUR_GOOGLE_API_KEY"; // optional if using gapi


export default function AuthFormWithDrive() {

  const [mode, setMode] = useState("login"); // 'login' or 'register'

  const [showModal, setShowModal] = useState(false);

  const [loading, setLoading] = useState(false);

  const [message, setMessage] = useState(null);


  const initial = {

    name: "",

    address: "",

    phone: "",

    email: "",

    password: "",

  };

  const [form, setForm] = useState(initial);


  // Simple client-side validation

  function validateForm(isRegister) {

    if (isRegister) {

      if (!form.name.trim()) return "Nama wajib diisi";

      if (!form.address.trim()) return "Alamat wajib diisi";

      if (!/^\+?[0-9\-\s]{7,20}$/.test(form.phone)) return "No HP tidak valid";

    }

    if (!/^[^@\s]+@[^@\s]+\.[^@\s]+$/.test(form.email)) return "Email tidak valid";

    if (form.password.length < 6) return "Password harus >= 6 karakter";

    return null;

  }


  function onChange(e) {

    setForm((s) => ({ ...s, [e.target.name]: e.target.value }));

  }


  async function onSubmit(e) {

    e.preventDefault();

    const err = validateForm(mode === "register");

    if (err) {

      setMessage({ type: "error", text: err });

      return;

    }

    setLoading(true);

    setMessage(null);


    try {

      // In real app: send to backend for account creation / login

      // For demo: save locally and to Google Drive (if authorized)

      const saved = saveToLocal(form, mode);

      setMessage({ type: "success", text: `Berhasil ${mode === "register" ? "registrasi" : "login"}` });


      // Attempt to upload to Google Drive (non-blocking)

      await saveToGoogleDrive(`${mode}-${Date.now()}.json`, JSON.stringify({ mode, form: saved }, null, 2));

    } catch (err) {

      console.error(err);

      setMessage({ type: "error", text: "Terjadi kesalahan saat memproses." });

    } finally {

      setLoading(false);

    }

  }


  function saveToLocal(formData, mode) {

    const key = `auth_demo_${mode}`;

    localStorage.setItem(key, JSON.stringify(formData));

    return formData;

  }


  // --------------------

  // Google Drive integration helper

  // --------------------

  // This component supplies a client-side helper to save a small file to Drive.

  // For this to work:

  // - Add GOOGLE_CLIENT_ID

  // - Serve your site on an origin configured in Google Console

  // - Use the "https://www.googleapis.com/auth/drive.file" scope


  const tokenRef = useRef(null);


  useEffect(() => {

    // Try to auto-load Google Identity Services if available

    if (typeof window !== "undefined" && window.google && GOOGLE_CLIENT_ID.includes("YOUR_") === false) {

      /* If using Google Identity Services for an OAuth token flow, you can initialize here.

         Example to prompt user to sign in and get access token:


         const client = google.accounts.oauth2.initTokenClient({

           client_id: GOOGLE_CLIENT_ID,

           scope: 'https://www.googleapis.com/auth/drive.file',

           callback: (tokenResponse) => { tokenRef.current = tokenResponse.access_token; }

         });


         // To request token later: client.requestAccessToken();

      */

    }

  }, []);


  async function saveToGoogleDrive(filename, content) {

    // If no client id configured, skip silently.

    if (GOOGLE_CLIENT_ID.includes("YOUR_")) {

      // not configured; show a hint

      console.info("Google Drive not configured. Skipping upload.");

      return null;

    }


    try {

      // If token is not present, request it using the popup flow (GSI token client)

      if (!tokenRef.current) {

        if (!window.google || !window.google.accounts || !window.google.accounts.oauth2) {

          console.warn("Google Identity Services not loaded. Include their script in your index.html:");

          console.warn("<script src=\"https://accounts.google.com/gsi/client\"></script>");

          return null;

        }


        const client = window.google.accounts.oauth2.initTokenClient({

          client_id: GOOGLE_CLIENT_ID,

          scope: "https://www.googleapis.com/auth/drive.file",

          callback: (resp) => {

            tokenRef.current = resp.access_token;

          },

        });


        // request token - this will show a popup managed by Google

        await new Promise((res) => client.requestAccessToken({ prompt: "consent" }, res));

        if (!tokenRef.current) return null;

      }


      // Now upload using the Drive REST API

      const metadata = {

        name: filename,

        mimeType: "application/json",

      };


      const boundary = "-------314159265358979323846";

      const delimiter = `\r\n--${boundary}\r\n`;

      const closeDelim = `\r\n--${boundary}--`;


      const multipartRequestBody =

        delimiter +

        'Content-Type: application/json; charset=UTF-8\r\n\r\n' +

        JSON.stringify(metadata) +

        '\r\n' +

        delimiter +

        'Content-Type: application/json\r\n\r\n' +

        content +

        closeDelim;


      const res = await fetch("https://www.googleapis.com/upload/drive/v3/files?uploadType=multipart", {

        method: "POST",

        headers: new Headers({

          Authorization: `Bearer ${tokenRef.current}`,

          "Content-Type": `multipart/related; boundary=${boundary}`,

        }),

        body: multipartRequestBody,

      });


      if (!res.ok) {

        console.warn("Drive upload failed", await res.text());

        return null;

      }

      const data = await res.json();

      console.info("Saved to Drive: ", data);

      return data;

    } catch (err) {

      console.error("Drive save error", err);

      return null;

    }

  }


  // --------------------

  // Social login handlers (placeholders)

  // --------------------

  function onSocialLogin(provider) {

    // For security and reliability, social OAuth should be implemented on the backend.

    // These examples simply open a new window to an endpoint you must provide.

    // Example: GET /auth/google -> redirect to Google OAuth flow -> redirect back with code -> exchange on server


    // Popup for example

    const w = window.open(`/auth/${provider}`, "_blank", "width=600,height=700");

    if (!w) setMessage({ type: "error", text: "Popup terblokir. Izinkan popup untuk melanjutkan." });

  }


  // --------------------

  // Small presentational components

  // --------------------

  const Input = ({ label, name, type = "text", placeholder = "" }) => (

    <label className="block">

      <div className="text-sm font-medium text-gray-700 mb-1">{label}</div>

      <input

        name={name}

        type={type}

        placeholder={placeholder}

        value={form[name]}

        onChange={onChange}

        className="w-full rounded-md border px-3 py-2 focus:outline-none focus:ring-2 focus:ring-indigo-400"

      />

    </label>

  );


  return (

    <div className="max-w-3xl mx-auto p-6">

      <h2 className="text-2xl font-semibold mb-4">Auth demo — Form Login & Registrasi</h2>


      {/* Toggle UI: inline vs modal */}

      <div className="flex gap-3 items-center mb-4">

        <div className="flex items-center gap-2">

          <input id="inline" type="radio" name="ui" defaultChecked onChange={() => setShowModal(false)} />

          <label htmlFor="inline">Tampilan biasa</label>

        </div>

        <div className="flex items-center gap-2">

          <input id="modal" type="radio" name="ui" onChange={() => setShowModal(true)} />

          <label htmlFor="modal">Tampilan popup</label>

        </div>


        <div className="ml-auto flex gap-2">

          <button

            onClick={() => setMode("login")}

            className={`px-3 py-1 rounded ${mode === "login" ? "bg-indigo-600 text-white" : "border"}`}

          >

            Login

          </button>

          <button

            onClick={() => setMode("register")}

            className={`px-3 py-1 rounded ${mode === "register" ? "bg-indigo-600 text-white" : "border"}`}

          >

            Registrasi

          </button>

        </div>

      </div>


      <div className="mb-4">

        <div className="text-sm text-gray-600">Masuk dengan akun:</div>

        <div className="flex gap-2 mt-2">

          <button onClick={() => onSocialLogin("google")} className="px-3 py-2 rounded border w-32">

            Google

          </button>

          <button onClick={() => onSocialLogin("facebook")} className="px-3 py-2 rounded border w-32">

            Facebook

          </button>

          <button onClick={() => onSocialLogin("github")} className="px-3 py-2 rounded border w-32">

            GitHub

          </button>

        </div>

      </div>


      {/* Inline form */}

      {!showModal && (

        <form onSubmit={onSubmit} className="bg-white p-6 border rounded-md shadow-sm">

          {mode === "register" && (

            <div className="grid grid-cols-1 gap-3 mb-3">

              <Input label="Nama" name="name" placeholder="Nama lengkap" />

              <Input label="Alamat" name="address" placeholder="Alamat rumah" />

              <Input label="No HP" name="phone" placeholder="contoh: +6281234..." />

            </div>

          )}


          <div className="grid grid-cols-1 gap-3 mb-3">

            <Input label="Email" name="email" type="email" placeholder="you@example.com" />

            <Input label="Password" name="password" type="password" placeholder="minimal 6 karakter" />

          </div>


          <div className="flex items-center gap-3">

            <button disabled={loading} className="px-4 py-2 rounded bg-indigo-600 text-white">

              {loading ? "Memproses..." : mode === "register" ? "Daftar" : "Masuk"}

            </button>

            <button

              type="button"

              onClick={() => { setForm(initial); setMessage(null); }}

              className="px-3 py-2 rounded border"

            >

              Reset

            </button>

            <button type="button" onClick={() => setShowModal(true)} className="ml-auto text-sm underline">

              Buka sebagai popup

            </button>

          </div>


          {message && (

            <div className={`mt-3 text-sm ${message.type === "error" ? "text-red-600" : "text-green-600"}`}>

              {message.text}

            </div>

          )}

        </form>

      )}


      {/* Modal / Popup form */}

      {showModal && (

        <div className="fixed inset-0 z-50 flex items-center justify-center">

          <div className="absolute inset-0 bg-black opacity-40" onClick={() => setShowModal(false)}></div>


          <div className="relative bg-white rounded-lg shadow-2xl w-full max-w-lg p-6 z-10">

            <div className="flex items-center justify-between mb-4">

              <h3 className="text-lg font-semibold">{mode === "register" ? "Registrasi" : "Login"} (Popup)</h3>

              <button onClick={() => setShowModal(false)} className="text-gray-500">✕</button>

            </div>


            <form onSubmit={onSubmit}>

              {mode === "register" && (

                <div className="grid grid-cols-1 gap-3 mb-3">

                  <Input label="Nama" name="name" placeholder="Nama lengkap" />

                  <Input label="Alamat" name="address" placeholder="Alamat rumah" />

                  <Input label="No HP" name="phone" placeholder="contoh: +6281234..." />

                </div>

              )}


              <div className="grid grid-cols-1 gap-3 mb-3">

                <Input label="Email" name="email" type="email" placeholder="you@example.com" />

                <Input label="Password" name="password" type="password" placeholder="minimal 6 karakter" />

              </div>


              <div className="flex items-center gap-3">

                <button disabled={loading} className="px-4 py-2 rounded bg-indigo-600 text-white">

                  {loading ? "Memproses..." : mode === "register" ? "Daftar" : "Masuk"}

                </button>

                <button type="button" onClick={() => { setForm(initial); setMessage(null); }} className="px-3 py-2 rounded border">

                  Reset

                </button>

                <button type="button" onClick={() => setShowModal(false)} className="ml-auto text-sm underline">

                  Tutup

                </button>

              </div>


              {message && (

                <div className={`mt-3 text-sm ${message.type === "error" ? "text-red-600" : "text-green-600"}`}>

                  {message.text}

                </div>

              )}

            </form>


            <div className="mt-4 text-sm text-gray-600">Atau daftar / masuk menggunakan:</div>

            <div className="flex gap-2 mt-2">

              <button onClick={() => onSocialLogin("google")} className="px-3 py-2 rounded border w-28">

                Google

              </button>

              <button onClick={() => onSocialLogin("facebook")} className="px-3 py-2 rounded border w-28">

                Facebook

              </button>

              <button onClick={() => onSocialLogin("github")} className="px-3 py-2 rounded border w-28">

                GitHub

              </button>

            </div>

          </div>

        </div>

      )}


      <div className="mt-6 text-sm text-gray-500">

        <strong>Catatan teknis:</strong>

        <ul className="list-disc list-inside">

          <li>Social sign-in flows (Google/Facebook/GitHub) perlu backend untuk menukar code -> access token secara aman.</li>

          <li>

            Google Drive upload di sini menggunakan akses token client-side. Untuk produksi: lakukan upload lewat server atau gunakan

            proper OAuth flow dan refresh token.

          </li>

          <li>Jangan menyimpan password plaintext di Google Drive atau localStorage di aplikasi nyata — ini contoh demonstrasi saja.</li>

        </ul>

      </div>

    </div>

  );

}



Selesai — aku sudah membuat komponen React untuk form login & registrasi (tampilan biasa + popup) dengan titik integrasi untuk Google Drive dan social login. Lihat file AuthFormWithDrive.jsx di canvas (di samping).

Kalau mau, aku bisa:

  • Menambahkan contoh backend (Express) untuk menangani OAuth Facebook/GitHub/Google.

  • Atau ubah komponen supaya menggunakan Google Picker / server-side upload secara lengkap.

Mau langkah selanjutnya yang mana?

Tags

Posting Komentar

0 Komentar

Posting Komentar (0)
3/related/default