Files
alexandrev-tibco bee80c7776
Build and push image / build (push) Successful in 1m15s
Add onboarding and viral share loop
2026-05-05 19:26:26 +02:00

126 lines
4.6 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"use client";
import { useState, type FormEvent } from "react";
import { trackEvent } from "@/lib/analytics-events";
import type { Dictionary } from "@/lib/i18n";
type FeedbackType = "bug" | "suggestion" | "general";
type FeedbackModalProps = {
dateKey: string;
onClose: () => void;
t: Dictionary;
};
export default function FeedbackModal({ dateKey, onClose, t }: FeedbackModalProps) {
const [type, setType] = useState<FeedbackType>("general");
const [message, setMessage] = useState("");
const [isSending, setIsSending] = useState(false);
const [status, setStatus] = useState<"idle" | "success" | "error">("idle");
async function submit(event: FormEvent<HTMLFormElement>) {
event.preventDefault();
if (!message.trim() || isSending) return;
setIsSending(true);
setStatus("idle");
try {
const response = await fetch("/api/feedback", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
dateKey,
message: message.trim(),
type,
page: window.location.pathname,
locale: window.navigator.language,
source: "hidden11",
}),
});
if (!response.ok) {
throw new Error("feedback request failed");
}
setStatus("success");
setMessage("");
trackEvent("feedback_sent", { type });
} catch {
setStatus("error");
} finally {
setIsSending(false);
}
}
return (
<div className="fixed inset-0 z-50 flex items-end bg-black/70 p-3 backdrop-blur-sm sm:items-center sm:justify-center">
<div className="w-full rounded-lg border border-white/10 bg-pitch-950 shadow-2xl sm:max-w-lg" role="dialog" aria-modal="true" aria-labelledby="feedback-title">
<div className="flex items-center justify-between gap-3 border-b border-white/10 p-4">
<div>
<h2 id="feedback-title" className="text-lg font-black text-white">
{t.feedback}
</h2>
<p className="mt-0.5 text-sm text-white/55">Tell us what to improve.</p>
</div>
<button
type="button"
onClick={onClose}
className="h-9 w-9 rounded-full border border-white/15 text-lg font-black text-white transition hover:bg-white/10"
aria-label={t.closeModal}
>
×
</button>
</div>
<form className="space-y-4 p-4" onSubmit={submit}>
<label className="block">
<span className="mb-2 block text-xs font-black uppercase text-white/55">{t.feedbackType}</span>
<select
value={type}
onChange={(event) => setType(event.target.value as FeedbackType)}
className="h-11 w-full rounded-lg border border-white/10 bg-white/[0.06] px-3 text-sm text-white outline-none"
>
<option value="bug">{t.feedbackBug}</option>
<option value="suggestion">{t.feedbackSuggestion}</option>
<option value="general">{t.feedbackGeneral}</option>
</select>
</label>
<label className="block">
<span className="mb-2 block text-xs font-black uppercase text-white/55">{t.feedbackMessage}</span>
<textarea
required
minLength={8}
value={message}
onChange={(event) => setMessage(event.target.value)}
placeholder={t.feedbackPlaceholder}
className="min-h-36 w-full rounded-lg border border-white/10 bg-white/[0.06] px-3 py-2 text-sm text-white outline-none placeholder:text-white/35"
/>
</label>
{status === "success" ? <p className="text-sm font-semibold text-emerald-200">{t.feedbackSuccess}</p> : null}
{status === "error" ? <p className="text-sm font-semibold text-red-200">{t.feedbackError}</p> : null}
<div className="flex items-center justify-end gap-2">
<button
type="button"
onClick={onClose}
className="h-10 rounded-full border border-white/10 px-4 text-sm font-black text-white transition hover:bg-white/10"
>
{t.close}
</button>
<button
type="submit"
disabled={isSending}
className="h-10 rounded-full bg-emerald-400 px-5 text-sm font-black text-pitch-950 transition hover:bg-emerald-300 disabled:cursor-not-allowed disabled:opacity-60"
>
{isSending ? "..." : t.feedbackSend}
</button>
</div>
</form>
</div>
</div>
);
}