124 lines
4.3 KiB
TypeScript
124 lines
4.3 KiB
TypeScript
import { useState } from "react";
|
|
import { useLocation } from "react-router-dom";
|
|
import { api } from "../lib/api.js";
|
|
|
|
export function FeedbackWidget() {
|
|
const location = useLocation();
|
|
const [open, setOpen] = useState(false);
|
|
const [message, setMessage] = useState("");
|
|
const [email, setEmail] = useState("");
|
|
const [rating, setRating] = useState<number | null>(null);
|
|
const [status, setStatus] = useState<"idle" | "sending" | "sent" | "error">("idle");
|
|
|
|
function reset() {
|
|
setMessage("");
|
|
setEmail("");
|
|
setRating(null);
|
|
setStatus("idle");
|
|
}
|
|
|
|
async function submit() {
|
|
if (!message.trim()) return;
|
|
setStatus("sending");
|
|
try {
|
|
await api.sendFeedback({
|
|
message: message.trim(),
|
|
email: email.trim() || undefined,
|
|
rating: rating ?? undefined,
|
|
page: location.pathname,
|
|
});
|
|
setStatus("sent");
|
|
} catch {
|
|
setStatus("error");
|
|
}
|
|
}
|
|
|
|
return (
|
|
<>
|
|
<button
|
|
onClick={() => setOpen(true)}
|
|
className="fixed bottom-4 right-4 z-40 rounded-full bg-accent px-4 py-2.5 text-sm font-medium text-white shadow-lg hover:opacity-90"
|
|
>
|
|
💬 Feedback
|
|
</button>
|
|
|
|
{open && (
|
|
<div className="fixed inset-0 z-50 flex items-end justify-end bg-black/40 p-4 sm:items-center sm:justify-center">
|
|
<div className="w-full max-w-sm rounded-lg border border-white/10 bg-panel p-4 shadow-xl">
|
|
{status === "sent" ? (
|
|
<div className="py-4 text-center">
|
|
<p className="text-sm text-slate-200">Obrigado pelo feedback! 🙏</p>
|
|
<button
|
|
onClick={() => {
|
|
reset();
|
|
setOpen(false);
|
|
}}
|
|
className="mt-3 rounded bg-accent px-3 py-1.5 text-xs font-medium text-white"
|
|
>
|
|
Fechar
|
|
</button>
|
|
</div>
|
|
) : (
|
|
<>
|
|
<div className="mb-3 flex items-center justify-between">
|
|
<h2 className="text-sm font-semibold text-white">Enviar feedback</h2>
|
|
<button
|
|
onClick={() => setOpen(false)}
|
|
className="text-slate-400 hover:text-white"
|
|
aria-label="Fechar"
|
|
>
|
|
✕
|
|
</button>
|
|
</div>
|
|
|
|
<div className="mb-3 flex gap-1">
|
|
{[1, 2, 3, 4, 5].map((n) => (
|
|
<button
|
|
key={n}
|
|
onClick={() => setRating(n)}
|
|
className={`text-lg ${rating && n <= rating ? "text-yellow-400" : "text-slate-600"}`}
|
|
aria-label={`Nota ${n}`}
|
|
>
|
|
★
|
|
</button>
|
|
))}
|
|
</div>
|
|
|
|
<textarea
|
|
value={message}
|
|
onChange={(e) => setMessage(e.target.value)}
|
|
placeholder="O que você achou? Encontrou algum erro ou tem alguma sugestão?"
|
|
rows={4}
|
|
className="w-full resize-none rounded border border-white/10 bg-black/20 p-2 text-sm text-white placeholder:text-slate-500 focus:outline-none focus:ring-1 focus:ring-accent"
|
|
/>
|
|
|
|
<input
|
|
type="email"
|
|
value={email}
|
|
onChange={(e) => setEmail(e.target.value)}
|
|
placeholder="Seu e-mail (opcional)"
|
|
className="mt-2 w-full rounded border border-white/10 bg-black/20 p-2 text-sm text-white placeholder:text-slate-500 focus:outline-none focus:ring-1 focus:ring-accent"
|
|
/>
|
|
|
|
{status === "error" && (
|
|
<p className="mt-2 text-xs text-red-400">
|
|
Não foi possível enviar. Tente novamente.
|
|
</p>
|
|
)}
|
|
|
|
<button
|
|
onClick={submit}
|
|
disabled={!message.trim() || status === "sending"}
|
|
className="mt-3 w-full rounded bg-accent py-2 text-sm font-medium text-white disabled:opacity-50"
|
|
>
|
|
{status === "sending" ? "Enviando..." : "Enviar"}
|
|
</button>
|
|
</>
|
|
)}
|
|
</div>
|
|
</div>
|
|
)}
|
|
</>
|
|
);
|
|
}
|