Skip to content

Commit 3cf0039

Browse files
committed
Move support ticket creation to /support/create-ticket page (#5144)
## Problem solved Short description of the bug fixed or feature added <!-- start pr-codex --> --- ## PR-Codex overview This PR focuses on restructuring the support ticket creation process in the dashboard application. It removes the old `CreateTicket` component and replaces it with a new implementation, enhancing user navigation and support form functionality. ### Detailed summary - Deleted `create-ticket.client.tsx`. - Updated `SupportPage` to replace `<CreateTicket />` with a button linking to `/support/create-ticket`. - Created a new `Page` component for the support ticket creation, including authentication check and breadcrumb navigation. - Introduced a new `CreateTicket` component with dynamic support form imports and form handling. - Added form reset and submission handling in `CreateTicket`. > ✨ Ask PR-Codex anything about this PR by commenting with `/codex {your question}` <!-- end pr-codex -->
1 parent 4841ff3 commit 3cf0039

File tree

6 files changed

+198
-190
lines changed

6 files changed

+198
-190
lines changed

apps/dashboard/src/@/constants/auth.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ const LOGGED_IN_ONLY_PATHS = [
55
"/team",
66
// anything that _starts_ with /cli is logged in only
77
"/cli",
8-
"/support",
98
// publish page
109
"/contracts/publish",
1110
];

apps/dashboard/src/app/(dashboard)/support/components/create-ticket.client.tsx

Lines changed: 0 additions & 187 deletions
This file was deleted.
Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
"use client";
2+
3+
import { Spinner } from "@/components/ui/Spinner/Spinner";
4+
import { Button } from "@/components/ui/button";
5+
import { Skeleton } from "@/components/ui/skeleton";
6+
import { SupportForm_SelectInput } from "components/help/contact-forms/shared/SupportForm_SelectInput";
7+
import dynamic from "next/dynamic";
8+
import { type ReactElement, useEffect, useRef, useState } from "react";
9+
import { useFormState, useFormStatus } from "react-dom";
10+
import { toast } from "sonner";
11+
import { createTicketAction } from "./create-ticket.action";
12+
13+
const ConnectSupportForm = dynamic(
14+
() => import("../../../../../components/help/contact-forms/connect"),
15+
{
16+
ssr: false,
17+
loading: () => <Skeleton className="h-12" />,
18+
},
19+
);
20+
const EngineSupportForm = dynamic(
21+
() => import("../../../../../components/help/contact-forms/engine"),
22+
{
23+
ssr: false,
24+
loading: () => <Skeleton className="h-12" />,
25+
},
26+
);
27+
const ContractSupportForm = dynamic(
28+
() => import("../../../../../components/help/contact-forms/contracts"),
29+
{
30+
ssr: false,
31+
loading: () => <Skeleton className="h-12" />,
32+
},
33+
);
34+
const AccountSupportForm = dynamic(
35+
() => import("../../../../../components/help/contact-forms/account"),
36+
{
37+
ssr: false,
38+
loading: () => <Skeleton className="h-12" />,
39+
},
40+
);
41+
const OtherSupportForm = dynamic(
42+
() => import("../../../../../components/help/contact-forms/other"),
43+
{
44+
ssr: false,
45+
loading: () => <Skeleton className="h-12" />,
46+
},
47+
);
48+
49+
const productOptions: { label: string; component: ReactElement }[] = [
50+
{
51+
label: "Connect",
52+
component: <ConnectSupportForm />,
53+
},
54+
{
55+
label: "Engine",
56+
component: <EngineSupportForm />,
57+
},
58+
{
59+
label: "Contracts",
60+
component: <ContractSupportForm />,
61+
},
62+
{
63+
label: "Account",
64+
component: <AccountSupportForm />,
65+
},
66+
{
67+
label: "Other",
68+
component: <OtherSupportForm />,
69+
},
70+
];
71+
72+
export function CreateTicket() {
73+
const formRef = useRef<HTMLFormElement>(null);
74+
const [productLabel, setProductLabel] = useState("");
75+
76+
const [state, formAction] = useFormState(createTicketAction, {
77+
message: "",
78+
success: false,
79+
});
80+
// needed here
81+
// eslint-disable-next-line no-restricted-syntax
82+
useEffect(() => {
83+
if (!state.message) {
84+
return;
85+
}
86+
if (state.success) {
87+
toast.success(state.message);
88+
} else {
89+
toast.error(state.message);
90+
}
91+
}, [state.success, state.message]);
92+
93+
return (
94+
<form
95+
ref={formRef}
96+
action={formAction}
97+
className="rounded-lg border bg-muted/50"
98+
>
99+
<div className="px-4 py-6 lg:px-6">
100+
<h2 className="font-semibold text-2xl tracking-tight">Get Support</h2>
101+
<p className="text-muted-foreground">
102+
We are here to help. Ask product questions, report problems, or leave
103+
feedback.
104+
</p>
105+
106+
<div className="h-7" />
107+
108+
<div className="flex flex-col gap-6">
109+
<SupportForm_SelectInput
110+
formLabel="What do you need help with?"
111+
name="product"
112+
options={productOptions.map((o) => o.label)}
113+
promptText="Select a product"
114+
onValueChange={setProductLabel}
115+
value={productLabel}
116+
required={true}
117+
/>
118+
{productOptions.find((o) => o.label === productLabel)?.component}
119+
</div>
120+
</div>
121+
122+
<div className="h-7" />
123+
124+
<div className="flex justify-end gap-3 border-t px-4 py-6 lg:px-6">
125+
<Button
126+
onClick={() => {
127+
formRef.current?.reset();
128+
setProductLabel("");
129+
}}
130+
variant="outline"
131+
>
132+
Reset
133+
</Button>
134+
<SubmitButton />
135+
</div>
136+
</form>
137+
);
138+
}
139+
140+
function SubmitButton() {
141+
const { pending } = useFormStatus();
142+
return (
143+
<Button
144+
type="submit"
145+
disabled={pending}
146+
className="flex min-w-24 flex-row gap-2"
147+
>
148+
{pending && <Spinner className="size-4" />}
149+
{pending ? "Submitting" : "Submit"}
150+
</Button>
151+
);
152+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import {
2+
Breadcrumb,
3+
BreadcrumbItem,
4+
BreadcrumbLink,
5+
BreadcrumbList,
6+
BreadcrumbPage,
7+
BreadcrumbSeparator,
8+
} from "@/components/ui/breadcrumb";
9+
import Link from "next/link";
10+
import { redirect } from "next/navigation";
11+
import { getAuthToken } from "../../../api/lib/getAuthToken";
12+
import { CreateTicket } from "./components/create-ticket.client";
13+
14+
export default function Page() {
15+
const authToken = getAuthToken();
16+
17+
if (!authToken) {
18+
return redirect(
19+
`/login?next=${encodeURIComponent("/support/create-ticket")}`,
20+
);
21+
}
22+
23+
return (
24+
<div>
25+
<Breadcrumb className="border-border border-b px-6 py-4">
26+
<BreadcrumbList>
27+
<BreadcrumbItem>
28+
<BreadcrumbLink asChild>
29+
<Link href="/support">Support</Link>
30+
</BreadcrumbLink>
31+
</BreadcrumbItem>
32+
<BreadcrumbSeparator />
33+
<BreadcrumbItem>
34+
<BreadcrumbPage>Get Support</BreadcrumbPage>
35+
</BreadcrumbItem>
36+
</BreadcrumbList>
37+
</Breadcrumb>
38+
<div className="container max-w-[750px] py-10">
39+
<CreateTicket />
40+
</div>
41+
</div>
42+
);
43+
}

0 commit comments

Comments
 (0)