"use client"; import React, { useEffect, useState } from "react"; import { FiX } from "react-icons/fi"; import { FaCircleCheck } from "react-icons/fa6"; import { submitHackReport } from "@/app/hack/[slug]/actions"; type ReportType = "hateful" | "harassment" | "misleading" | "stolen"; interface ReportModalProps { slug: string; onClose: () => void; } const ReportModal: React.FC = ({ slug, onClose }) => { const [currentPage, setCurrentPage] = useState<"select" | "details" | "success">("select"); const [reportType, setReportType] = useState(null); const [details, setDetails] = useState(""); const [email, setEmail] = useState(""); const [isImpersonating, setIsImpersonating] = useState(false); const [isSubmitting, setIsSubmitting] = useState(false); const [error, setError] = useState(null); useEffect(() => { const html = document.documentElement; const body = document.body; const previousHtmlOverflow = html.style.overflow; const previousBodyOverflow = body.style.overflow; const previousBodyPaddingRight = body.style.paddingRight; const scrollBarWidth = window.innerWidth - html.clientWidth; html.style.overflow = "hidden"; body.style.overflow = "hidden"; if (scrollBarWidth > 0) { body.style.paddingRight = `${scrollBarWidth}px`; } return () => { html.style.overflow = previousHtmlOverflow; body.style.overflow = previousBodyOverflow; body.style.paddingRight = previousBodyPaddingRight; }; }, []); const canSubmit = () => { if (!reportType) return false; if (reportType === "stolen") { // Stolen requires email and details if (!email.trim() || !details.trim()) return false; // Basic email validation (matches server-side validation pattern) const emailRegex = /^(?!\.)(?!.*\.\.)([a-z0-9_'+\-\.]*)[a-z0-9_'+\-]@([a-z0-9][a-z0-9\-]*\.)+[a-z]{2,}$/; if (!emailRegex.test(email.trim().toLowerCase())) return false; return true; } if (reportType === "misleading") { // Misleading requires details return details.trim().length > 0; } // Hateful and Harassment are optional, so can always submit return true; }; const handleSubmit = async () => { if (!canSubmit() || !reportType) return; setIsSubmitting(true); setError(null); try { const result = await submitHackReport({ slug, reportType, details: details.trim() || null, email: reportType === "stolen" ? email.trim() : null, isImpersonating: reportType === "stolen" ? isImpersonating : null, }); if (result.error) { setError(result.error); setIsSubmitting(false); } else { setCurrentPage("success"); setIsSubmitting(false); } } catch (e) { setError("Failed to submit report. Please try again."); setIsSubmitting(false); } }; const renderSelectPage = () => (
Report Hack

Please select the reason for reporting this hack.

); const renderSuccessPage = () => (
Report Submitted

Thank you for your report. We will review it and take appropriate action.

Report successfully sent!

); const renderDetailsPage = () => { const isStolen = reportType === "stolen"; const isMisleading = reportType === "misleading"; const requiresDetails = isStolen || isMisleading; const isOptional = reportType === "hateful" || reportType === "harassment"; return (
{reportType === "hateful" && "Hateful Content"} {reportType === "harassment" && "Harassment"} {reportType === "misleading" && "Misleading"} {reportType === "stolen" && "My Hack Was Stolen"}

{isStolen ? "Please provide your contact information and details about the stolen hack." : isOptional ? "Please provide additional details (optional)." : "Please provide additional details."}

{isStolen && ( <>
setEmail(e.target.value)} placeholder="your@email.com" className="w-full px-3 py-2 rounded-md border border-[var(--border)] bg-[var(--surface-1)] text-sm focus:outline-none focus:ring-2 focus:ring-[var(--accent)]" />
)}