Claude Code · claude-opus-5-5 · React (TSX)
결과 코드펼치기
import { useRef, useState } from "react";
const REASONS = [
{ id: "ad", label: "광고·홍보", hint: "가게나 상품을 홍보하는 글" },
{ id: "abuse", label: "욕설·비방", hint: "사람을 깎아내리거나 모욕하는 표현" },
{ id: "fake", label: "허위 정보", hint: "방문하지 않고 쓴 것 같은 리뷰" },
{ id: "other", label: "기타", hint: "위에 없는 이유" },
];
export default function ReviewReportDialog() {
const dialog = useRef<HTMLDialogElement>(null);
const [reason, setReason] = useState<string | null>(null);
const [detail, setDetail] = useState("");
const [error, setError] = useState<string | null>(null);
const [sent, setSent] = useState(false);
function submit() {
if (!reason) return setError("신고 사유를 선택해 주세요.");
if (reason === "other" && !detail.trim()) return setError("기타 사유는 설명을 적어 주세요.");
setSent(true);
dialog.current?.close();
}
return (
<section className="w-full bg-stone-100 px-4 py-14 text-stone-900 sm:px-8">
<article className="mx-auto max-w-xl rounded-2xl border border-stone-200 bg-white p-5 shadow-sm">
<div className="flex items-center gap-3">
<span className="flex h-9 w-9 items-center justify-center rounded-full bg-orange-100 text-sm font-semibold text-orange-700" aria-hidden>
맛
</span>
<div className="min-w-0 flex-1">
<p className="text-sm font-semibold">맛집탐방러</p>
<p className="text-xs text-stone-500">을지로 국숫집 · 3일 전</p>
</div>
<button
type="button"
disabled={sent}
onClick={() => {
setError(null);
dialog.current?.showModal();
}}
className="inline-flex h-8 items-center gap-1 rounded-lg px-2.5 text-[13px] text-stone-500 transition hover:bg-stone-100 hover:text-stone-900 disabled:pointer-events-none disabled:opacity-60"
>
<svg width="14" height="14" viewBox="0 0 16 16" fill="none" aria-hidden>
<path d="M3.5 14V2.5M3.5 3h8l-1.5 3 1.5 3h-8" stroke="currentColor" strokeWidth="1.4" strokeLinecap="round" strokeLinejoin="round" />
</svg>
{sent ? "신고 접수됨" : "신고"}
</button>
</div>
<p className="mt-4 text-[15px] leading-relaxed">
★★★★★ 여기 무조건 가세요!! 할인 쿠폰은 아래 링크에서 받으시면 됩니다 → bit.ly/xxxx
</p>
</article>
<dialog
ref={dialog}
aria-labelledby="report-title"
className="m-auto w-[min(28rem,calc(100%-2rem))] rounded-2xl border border-stone-200 bg-white p-0 text-stone-900 shadow-2xl backdrop:bg-black/50 backdrop:backdrop-blur-[2px]"
>
<form
method="dialog"
className="space-y-4 p-6"
onSubmit={(e) => {
e.preventDefault();
submit();
}}
>
<div>
<h2 id="report-title" className="text-lg font-semibold">
리뷰 신고
</h2>
<p className="mt-1 text-sm text-stone-500">운영팀이 확인한 뒤 규칙에 맞지 않으면 숨깁니다.</p>
</div>
<fieldset className="space-y-2">
<legend className="sr-only">신고 사유</legend>
{REASONS.map((r) => (
<label
key={r.id}
className={`flex cursor-pointer items-start gap-3 rounded-xl border px-3.5 py-3 transition-colors ${
reason === r.id ? "border-orange-400 bg-orange-50" : "border-stone-200 hover:border-stone-300"
}`}
>
<input
type="radio"
name="reason"
value={r.id}
checked={reason === r.id}
onChange={() => {
setReason(r.id);
setError(null);
}}
className="mt-0.5 h-4 w-4 accent-orange-500"
/>
<span>
<span className="block text-sm font-medium">{r.label}</span>
<span className="block text-xs text-stone-500">{r.hint}</span>
</span>
</label>
))}
</fieldset>
<div>
<label htmlFor="report-detail" className="mb-1.5 block text-[13px] font-medium text-stone-600">
설명 {reason === "other" ? "(필수)" : "(선택)"}
</label>
<textarea
id="report-detail"
rows={3}
maxLength={500}
value={detail}
onChange={(e) => setDetail(e.target.value)}
placeholder="어떤 점이 문제인지 알려 주세요"
className="w-full resize-none rounded-xl border border-stone-200 px-3 py-2 text-sm outline-none transition placeholder:text-stone-400 focus:border-orange-400 focus:ring-4 focus:ring-orange-100"
/>
</div>
{error && (
<p role="alert" className="text-sm text-red-600">
{error}
</p>
)}
<div className="flex justify-end gap-2">
<button type="button" onClick={() => dialog.current?.close()} className="h-10 rounded-xl px-4 text-sm font-medium text-stone-600 hover:bg-stone-100">
취소
</button>
<button type="submit" className="h-10 rounded-xl bg-stone-900 px-4 text-sm font-medium text-white transition hover:bg-stone-700 active:scale-[0.98]">
신고하기
</button>
</div>
</form>
</dialog>
</section>
);
}
적용 사이트 0
이 블록이 실제로 쓰인 사이트입니다.
아직 등록된 사이트가 없습니다. 이 블록을 쓴 사이트가 있다면 알려 주세요.
재현 후기
v1 · 0개아직 이 버전의 프롬프트로 직접 만들어 본 사람이 없습니다. 첫 번째로 재현해 보세요.