172 lines
6.3 KiB
TypeScript
172 lines
6.3 KiB
TypeScript
import { useEffect, useState } from "react";
|
||
import Navigation from "@/components/landing/Navigation";
|
||
import Footer from "@/components/landing/Footer";
|
||
import FAQSection from "@/components/landing/FAQSection";
|
||
import { Button } from "@/components/ui/button";
|
||
import { Input } from "@/components/ui/input";
|
||
import { Textarea } from "@/components/ui/textarea";
|
||
import { apiRequest, getQueryFn } from "@/lib/queryClient";
|
||
import { toast } from "@/hooks/use-toast";
|
||
import { useQuery } from "@tanstack/react-query";
|
||
|
||
type SessionUser = {
|
||
id: string;
|
||
username: string;
|
||
};
|
||
|
||
export default function SupportPage() {
|
||
const [form, setForm] = useState({
|
||
name: "",
|
||
email: "",
|
||
subject: "",
|
||
message: "",
|
||
});
|
||
const [ticketId, setTicketId] = useState("");
|
||
const [statusEmail, setStatusEmail] = useState("");
|
||
const [statusResult, setStatusResult] = useState<null | {
|
||
ticket_id: string;
|
||
status: string;
|
||
created_at?: string;
|
||
updated_at?: string;
|
||
}>(null);
|
||
const [submitting, setSubmitting] = useState(false);
|
||
const [checking, setChecking] = useState(false);
|
||
const { data: sessionUser } = useQuery<SessionUser | null>({
|
||
queryKey: ["/me"],
|
||
queryFn: getQueryFn<SessionUser | null>({ on401: "returnNull" }),
|
||
});
|
||
const sessionEmail = sessionUser?.username?.trim() || "";
|
||
|
||
useEffect(() => {
|
||
if (sessionEmail) {
|
||
setForm((prev) => ({ ...prev, email: sessionEmail }));
|
||
setStatusEmail(sessionEmail);
|
||
}
|
||
}, [sessionEmail]);
|
||
|
||
const submitTicket = async () => {
|
||
const effectiveEmail = sessionEmail || form.email.trim();
|
||
if (!form.name.trim() || !effectiveEmail || !form.subject.trim() || !form.message.trim()) {
|
||
toast({ title: "Please fill all fields." });
|
||
return;
|
||
}
|
||
setSubmitting(true);
|
||
try {
|
||
const res = await apiRequest("POST", "/support/ticket", {
|
||
...form,
|
||
email: effectiveEmail,
|
||
});
|
||
const data = await res.json();
|
||
setTicketId(data.ticket_id || "");
|
||
toast({ title: "Ticket created", description: `Ticket ID: ${data.ticket_id}` });
|
||
setForm({ name: "", email: "", subject: "", message: "" });
|
||
} catch (err: any) {
|
||
toast({ title: "Failed to create ticket", description: err?.message || "Try again." });
|
||
} finally {
|
||
setSubmitting(false);
|
||
}
|
||
};
|
||
|
||
const checkStatus = async () => {
|
||
const effectiveEmail = sessionEmail || statusEmail.trim();
|
||
if (!ticketId.trim() || !effectiveEmail) {
|
||
toast({ title: "Enter ticket ID and email." });
|
||
return;
|
||
}
|
||
setChecking(true);
|
||
try {
|
||
const res = await apiRequest("POST", `/support/ticket/status/${ticketId.trim()}`, {
|
||
email: effectiveEmail,
|
||
});
|
||
const data = await res.json();
|
||
setStatusResult(data);
|
||
} catch (err: any) {
|
||
setStatusResult(null);
|
||
toast({ title: "Ticket not found", description: "Check ticket ID and email." });
|
||
} finally {
|
||
setChecking(false);
|
||
}
|
||
};
|
||
|
||
return (
|
||
<div className="min-h-screen text-foreground">
|
||
<Navigation />
|
||
<main className="pt-24">
|
||
<section className="py-24 px-6 bg-card/10">
|
||
<div className="max-w-5xl mx-auto grid gap-10 lg:grid-cols-2">
|
||
<div className="space-y-6">
|
||
<h1 className="text-4xl md:text-5xl font-bold">Support Center</h1>
|
||
<p className="text-muted-foreground text-lg">
|
||
Need help? Create a support ticket and we’ll get back to you.
|
||
</p>
|
||
<div className="rounded-2xl border border-card-border bg-card/60 p-6 space-y-4">
|
||
<h2 className="text-xl font-semibold">Contact support</h2>
|
||
<div className="grid gap-4">
|
||
<Input
|
||
placeholder="Full name"
|
||
value={form.name}
|
||
onChange={(e) => setForm((p) => ({ ...p, name: e.target.value }))}
|
||
/>
|
||
<Input
|
||
placeholder="Email"
|
||
value={form.email}
|
||
onChange={(e) => setForm((p) => ({ ...p, email: e.target.value }))}
|
||
disabled={Boolean(sessionEmail)}
|
||
/>
|
||
<Input
|
||
placeholder="Subject"
|
||
value={form.subject}
|
||
onChange={(e) => setForm((p) => ({ ...p, subject: e.target.value }))}
|
||
/>
|
||
<Textarea
|
||
placeholder="Describe your issue"
|
||
value={form.message}
|
||
onChange={(e) => setForm((p) => ({ ...p, message: e.target.value }))}
|
||
rows={5}
|
||
/>
|
||
<Button onClick={submitTicket} disabled={submitting}>
|
||
{submitting ? "Submitting..." : "Submit ticket"}
|
||
</Button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div className="space-y-6">
|
||
<div className="rounded-2xl border border-card-border bg-card/60 p-6 space-y-4">
|
||
<h2 className="text-xl font-semibold">Check ticket status</h2>
|
||
<Input
|
||
placeholder="Ticket ID"
|
||
value={ticketId}
|
||
onChange={(e) => setTicketId(e.target.value)}
|
||
/>
|
||
<Input
|
||
placeholder="Email used for ticket"
|
||
value={statusEmail}
|
||
onChange={(e) => setStatusEmail(e.target.value)}
|
||
disabled={Boolean(sessionEmail)}
|
||
/>
|
||
<Button onClick={checkStatus} disabled={checking}>
|
||
{checking ? "Checking..." : "Check status"}
|
||
</Button>
|
||
{statusResult && (
|
||
<div className="rounded-xl border border-card-border bg-black/30 p-4 text-sm">
|
||
<div><strong>Status:</strong> {statusResult.status}</div>
|
||
{statusResult.created_at && (
|
||
<div><strong>Created:</strong> {statusResult.created_at}</div>
|
||
)}
|
||
{statusResult.updated_at && (
|
||
<div><strong>Updated:</strong> {statusResult.updated_at}</div>
|
||
)}
|
||
</div>
|
||
)}
|
||
</div>
|
||
<FAQSection />
|
||
</div>
|
||
</div>
|
||
</section>
|
||
</main>
|
||
<Footer />
|
||
</div>
|
||
);
|
||
}
|