chrysopedia/frontend/src/components/ReportIssueModal.tsx
jlightner c6efec8363 feat: Split key moment card header into standalone h3 title and flex-ro…
- "frontend/src/pages/TechniquePage.tsx"
- "frontend/src/App.css"

GSD-Task: S03/T01
2026-03-30 08:55:48 +00:00

135 lines
4.2 KiB
TypeScript

import { useState } from "react";
import { submitReport, type ContentReportCreate } from "../api/public-client";
interface ReportIssueModalProps {
contentType: string;
contentId?: string | null;
contentTitle?: string | null;
onClose: () => void;
}
const REPORT_TYPES = [
{ value: "inaccurate", label: "Inaccurate content" },
{ value: "missing_info", label: "Missing information" },
{ value: "wrong_attribution", label: "Wrong attribution" },
{ value: "formatting", label: "Formatting issue" },
{ value: "other", label: "Other" },
];
export default function ReportIssueModal({
contentType,
contentId,
contentTitle,
onClose,
}: ReportIssueModalProps) {
const [reportType, setReportType] = useState("inaccurate");
const [description, setDescription] = useState("");
const [submitting, setSubmitting] = useState(false);
const [submitted, setSubmitted] = useState(false);
const [error, setError] = useState<string | null>(null);
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
if (description.trim().length < 10) {
setError("Please provide at least 10 characters describing the issue.");
return;
}
setSubmitting(true);
setError(null);
try {
const body: ContentReportCreate = {
content_type: contentType,
content_id: contentId ?? null,
content_title: contentTitle ?? null,
report_type: reportType,
description: description.trim(),
page_url: window.location.href,
};
await submitReport(body);
setSubmitted(true);
} catch (err) {
setError(
err instanceof Error ? err.message : "Failed to submit report",
);
} finally {
setSubmitting(false);
}
};
return (
<div className="modal-overlay" onClick={onClose}>
<div className="modal-content report-modal" onClick={(e) => e.stopPropagation()}>
{submitted ? (
<>
<h3 className="report-modal__title">Thank you</h3>
<p className="report-modal__success">
Your report has been submitted. We'll review it shortly.
</p>
<button className="btn btn--primary" onClick={onClose}>
Close
</button>
</>
) : (
<>
<h3 className="report-modal__title">Report an issue</h3>
{contentTitle && (
<p className="report-modal__context">
About: <strong>{contentTitle}</strong>
</p>
)}
<form onSubmit={handleSubmit}>
<label className="report-modal__label">
Issue type
<select
className="report-modal__select"
value={reportType}
onChange={(e) => setReportType(e.target.value)}
>
{REPORT_TYPES.map((t) => (
<option key={t.value} value={t.value}>
{t.label}
</option>
))}
</select>
</label>
<label className="report-modal__label">
Description
<textarea
className="report-modal__textarea"
value={description}
onChange={(e) => setDescription(e.target.value)}
placeholder="Describe the issue…"
rows={4}
maxLength={2000}
/>
</label>
{error && <p className="report-modal__error">{error}</p>}
<div className="report-modal__actions">
<button
type="button"
className="btn btn--secondary"
onClick={onClose}
disabled={submitting}
>
Cancel
</button>
<button
type="submit"
className="btn btn--primary"
disabled={submitting || description.trim().length < 10}
>
{submitting ? "Submitting…" : "Submit report"}
</button>
</div>
</form>
</>
)}
</div>
</div>
);
}