79760187

Date: 2025-09-09 18:47:28
Score: 0.5
Natty:
Report link
import React, { useState } from "react";

// MultiStepFormWithProgressAndAccordion.jsx
// Single-file React component (TailwindCSS required in host project)
// Features:
// - 3-step form with validation
// - Top progress bar (percentage)
// - After final submit, display an accordion for each step
//   that shows: submitted values, step completion percentage, and backlink field
// - Uses Tailwind classes for styling and framer-motion for subtle animations (optional)

export default function MultiStepFormWithProgressAndAccordion() {
  const steps = [
    {
      id: 1,
      title: "Your Info",
      fields: [
        { name: "name", label: "Name", placeholder: "Your full name" },
        { name: "email", label: "Email", placeholder: "[email protected]" },
      ],
    },
    {
      id: 2,
      title: "Website / Backlink",
      fields: [
        { name: "website", label: "Website URL", placeholder: "https://example.com" },
        { name: "anchor", label: "Anchor Text", placeholder: "Example Anchor" },
      ],
    },
    {
      id: 3,
      title: "Answer & Tags",
      fields: [
        { name: "answer", label: "Answer (short)", placeholder: "Write your answer here...", type: "textarea" },
        { name: "tags", label: "Tags (comma)", placeholder: "tag1, tag2" },
      ],
    },
  ];

  // initialize form data
  const initialData = {};
  steps.forEach((s) => s.fields.forEach((f) => (initialData[f.name] = "")));

  const [currentStep, setCurrentStep] = useState(0);
  const [formData, setFormData] = useState(initialData);
  const [submitted, setSubmitted] = useState(false);
  const [expanded, setExpanded] = useState({});

  function handleChange(e) {
    const { name, value } = e.target;
    setFormData((p) => ({ ...p, [name]: value }));
  }

  function stepCompletionPercent(stepIndex) {
    const fields = steps[stepIndex].fields;
    const total = fields.length;
    let filled = 0;
    fields.forEach((f) => {
      const v = (formData[f.name] || "").toString().trim();
      if (v.length > 0) filled += 1;
    });
    return Math.round((filled / total) * 100);
  }

  function overallProgressPercent() {
    const totalFields = steps.reduce((acc, s) => acc + s.fields.length, 0);
    const filled = Object.values(formData).filter((v) => (v || "").toString().trim().length > 0).length;
    return Math.round((filled / totalFields) * 100);
  }

  function nextStep() {
    if (currentStep < steps.length - 1) setCurrentStep((s) => s + 1);
  }
  function prevStep() {
    if (currentStep > 0) setCurrentStep((s) => s - 1);
  }

  function validateStep(index) {
    // simple required check for demonstration
    const fields = steps[index].fields;
    for (const f of fields) {
      if (!formData[f.name] || formData[f.name].toString().trim() === "") return false;
    }
    return true;
  }

  function handleSubmit(e) {
    e.preventDefault();
    // Validate all steps
    for (let i = 0; i < steps.length; i++) {
      if (!validateStep(i)) {
        setCurrentStep(i);
        alert(`Please complete "${steps[i].title}" before submitting.`);
        return;
      }
    }

    // Simulate submission (e.g., POST to your API)
    // For this component we mark as submitted and show accordions with percentages per step
    setSubmitted(true);

    // expand all accordions by default after submit
    const ex = {};
    steps.forEach((s) => (ex[s.id] = true));
    setExpanded(ex);
  }

  function toggleAccordion(stepId) {
    setExpanded((p) => ({ ...p, [stepId]: !p[stepId] }));
  }

  return (
    <div className="max-w-3xl mx-auto p-4">
      <h2 className="text-2xl font-semibold mb-4">Multi-step Answer Submission (Backlink)</h2>

      {/* Progress bar */}
      <div className="mb-4">
        <div className="flex items-center justify-between text-sm mb-1">
          <span>Progress</span>
          <span className="font-medium">{overallProgressPercent()}%</span>
        </div>
        <div className="bg-gray-200 rounded-full h-3 overflow-hidden">
          <div
            className="h-3 rounded-full transition-all duration-500"
            style={{ width: `${overallProgressPercent()}%`, background: "linear-gradient(90deg,#4f46e5,#06b6d4)" }}
          />
        </div>
      </div>

      {!submitted ? (
        <form onSubmit={handleSubmit} className="bg-white rounded-lg shadow p-6">
          <div className="mb-6">
            <div className="flex gap-2 items-center text-sm">
              {steps.map((s, idx) => (
                <div key={s.id} className={`flex-1 text-center p-2 rounded ${idx === currentStep ? "bg-indigo-50" : ""}`}>
                  <div className="font-medium">Step {idx + 1}</div>
                  <div className="text-xs text-gray-500">{s.title}</div>
                  <div className="mt-1 text-xs">{stepCompletionPercent(idx)}%</div>
                </div>
              ))}
            </div>
          </div>

          <div>
            <h3 className="font-semibold mb-3">{steps[currentStep].title}</h3>
            <div className="space-y-4">
              {steps[currentStep].fields.map((f) => (
                <div key={f.name}>
                  <label className="block text-sm font-medium mb-1">{f.label}</label>
                  {f.type === "textarea" ? (
                    <textarea
                      name={f.name}
                      rows={4}
                      placeholder={f.placeholder}
                      value={formData[f.name]}
                      onChange={handleChange}
                      className="w-full border rounded p-2"
                    />
                  ) : (
                    <input
                      name={f.name}
                      placeholder={f.placeholder}
                      value={formData[f.name]}
                      onChange={handleChange}
                      className="w-full border rounded p-2"
                    />
                  )}
                </div>
              ))}
            </div>
          </div>

          <div className="mt-6 flex justify-between">
            <div>
              <button type="button" onClick={prevStep} disabled={currentStep === 0} className="px-4 py-2 rounded-md border">
                Back
              </button>
            </div>
            <div className="flex gap-2">
              {currentStep < steps.length - 1 ? (
                <button
                  type="button"
                  onClick={() => {
                    if (validateStep(currentStep)) nextStep();
                    else alert("Please fill required fields in this step.");
                  }}
                  className="px-4 py-2 rounded-md bg-indigo-600 text-white"
                >
                  Next
                </button>
              ) : (
                <button type="submit" className="px-4 py-2 rounded-md bg-green-600 text-white">
                  Submit Answer & Create Backlink
                </button>
              )}
            </div>
          </div>
        </form>
      ) : (
        <div className="space-y-4">
          <div className="bg-white rounded-lg shadow p-4">
            <div className="flex items-center justify-between mb-2">
              <div>
                <h3 className="font-semibold">Submission Complete</h3>
                <p className="text-sm text-gray-600">Your answer has been submitted. Below are details by step and completion percentage.</p>
              </div>
              <div className="text-right">
                <div className="text-sm">Overall: <span className="font-medium">{overallProgressPercent()}%</span></div>
              </div>
            </div>
          </div>

          {/* Accordions for each step */}
          {steps.map((s, idx) => (
            <div key={s.id} className="bg-white rounded-lg shadow">
              <button
                type="button"
                onClick={() => toggleAccordion(s.id)}
                className="w-full text-left p-4 flex items-center justify-between"
              >
                <div>
                  <div className="font-medium">{s.title}</div>
                  <div className="text-xs text-gray-500">Step {idx + 1} • {stepCompletionPercent(idx)}% complete</div>
                </div>
                <div className="text-sm">{expanded[s.id] ? "−" : "+"}</div>
              </button>

              {expanded[s.id] && (
                <div className="p-4 border-t">
                  <div className="grid gap-3">
                    {s.fields.map((f) => (
                      <div key={f.name} className="">
                        <div className="text-xs text-gray-500">{f.label}</div>
                        <div className="mt-1 break-words">{formData[f.name] || <span className="text-gray-400">(empty)</span>}</div>
                      </div>
                    ))}

                    <div className="pt-2">
                      <div className="text-xs text-gray-500">Step progress</div>
                      <div className="mt-1 w-full bg-gray-100 rounded-full h-2 overflow-hidden">
                        <div style={{ width: `${stepCompletionPercent(idx)}%` }} className="h-2 rounded-full transition-all" />
                      </div>
                    </div>

                    {/* Quick actions: copy backlink, open website */}
                    {s.fields.find((ff) => ff.name === "website") && (
                      <div className="flex gap-2 mt-3">
                        <a
                          href={formData.website || "#"}
                          target="_blank"
                          rel="noreferrer"
                          className="px-3 py-2 rounded border text-sm"
                        >
                          Open Link
                        </a>
                        <button
                          type="button"
                          onClick={() => navigator.clipboard && navigator.clipboard.writeText(formData.website || "")}
                          className="px-3 py-2 rounded border text-sm"
                        >
                          Copy URL
                        </button>
                      </div>
                    )}
                  </div>
                </div>
              )}
            </div>
          ))}

          <div className="flex gap-2 mt-4">
            <button
              className="px-4 py-2 rounded border"
              onClick={() => {
                // reset to start a new submission
                setFormData(initialData);
                setSubmitted(false);
                setCurrentStep(0);
                setExpanded({});
              }}
            >
              Submit Another
            </button>
            <button
              className="px-4 py-2 rounded bg-indigo-600 text-white"
              onClick={() => alert("Implement real API POST in handleSubmit to actually create backlinks on your site.")}
            >
              Integrate with API
            </button>
          </div>
        </div>
      )}

      <div className="mt-6 text-xs text-gray-500">Tip: Hook handleSubmit to your backend (fetch/axios) to actually persist answers and create backlinks on your site.</div>
    </div>
  );
}

For More Information

Reasons:
  • Probably link only (1):
  • Long answer (-1):
  • Has code block (-0.5):
  • Low reputation (1):
Posted by: Nazmus Sakib