From cf53184bb161ed98146343e24586dae7804f6459 Mon Sep 17 00:00:00 2001 From: MananTank Date: Mon, 10 Feb 2025 22:21:18 +0000 Subject: [PATCH] [TOOL-3379] Dashboard: Add team selector in support form (#6227) --- apps/dashboard/src/@/api/team.ts | 4 ++ .../components/create-ticket.action.ts | 71 ++++++++++--------- .../components/create-ticket.client.tsx | 57 ++++++++++++--- .../support/create-ticket/page.tsx | 21 +++--- .../shared/SupportForm_SelectInput.tsx | 12 +--- .../shared/SupportForm_TeamSelection.tsx | 57 +++++++++++++++ 6 files changed, 161 insertions(+), 61 deletions(-) create mode 100644 apps/dashboard/src/components/help/contact-forms/shared/SupportForm_TeamSelection.tsx diff --git a/apps/dashboard/src/@/api/team.ts b/apps/dashboard/src/@/api/team.ts index 78ea7550851..781d216bcf4 100644 --- a/apps/dashboard/src/@/api/team.ts +++ b/apps/dashboard/src/@/api/team.ts @@ -47,6 +47,10 @@ export async function getTeamBySlug(slug: string) { return null; } +export function getTeamById(id: string) { + return getTeamBySlug(id); +} + export async function getTeams() { const token = await getAuthToken(); if (!token) { diff --git a/apps/dashboard/src/app/(dashboard)/support/create-ticket/components/create-ticket.action.ts b/apps/dashboard/src/app/(dashboard)/support/create-ticket/components/create-ticket.action.ts index a18e208994d..90e7afbb153 100644 --- a/apps/dashboard/src/app/(dashboard)/support/create-ticket/components/create-ticket.action.ts +++ b/apps/dashboard/src/app/(dashboard)/support/create-ticket/components/create-ticket.action.ts @@ -1,10 +1,10 @@ "use server"; import "server-only"; -import { COOKIE_ACTIVE_ACCOUNT, COOKIE_PREFIX_TOKEN } from "@/constants/cookie"; -import { API_SERVER_URL } from "@/constants/env"; -import { cookies } from "next/headers"; -import { redirect } from "next/navigation"; +import { getTeamById } from "@/api/team"; +import { getRawAccount } from "../../../../account/settings/getAccount"; +import { getAuthTokenWalletAddress } from "../../../../api/lib/getAuthToken"; +import { loginRedirect } from "../../../../login/loginRedirect"; type State = { success: boolean; @@ -71,32 +71,35 @@ export async function createTicketAction( _previousState: State, formData: FormData, ) { - const cookieManager = await cookies(); - const activeAccount = cookieManager.get(COOKIE_ACTIVE_ACCOUNT)?.value; - const token = activeAccount - ? cookieManager.get(COOKIE_PREFIX_TOKEN + activeAccount)?.value - : null; - if (!activeAccount || !token) { - // user is not logged in, make them log in - redirect(`/login?next=${encodeURIComponent("/support")}`); + const teamId = formData.get("teamId")?.toString(); + + if (!teamId) { + return { + success: false, + message: "teamId is required", + }; } - const accountRes = await fetch(`${API_SERVER_URL}/v1/account/me`, { - method: "GET", - headers: { - Authorization: `Bearer ${token}`, - }, - }); - if (accountRes.status !== 200) { - // user is not logged in, make them log in - redirect(`/login?next=${encodeURIComponent("/support")}`); + + const team = await getTeamById(teamId); + + if (!team) { + return { + success: false, + message: `Team with id "${teamId}" not found`, + }; } - const account = (await accountRes.json()) as { - data: { name: string; email: string; plan: string; id: string }; - }; + const [walletAddress, account] = await Promise.all([ + getAuthTokenWalletAddress(), + getRawAccount(), + ]); + + if (!walletAddress || !account) { + loginRedirect("/support"); + } - const customerId = isValidPlan(account.data.plan) - ? planToCustomerId[account.data.plan] + const customerId = isValidPlan(team.billingPlan) + ? planToCustomerId[team.billingPlan] : undefined; const product = formData.get("product")?.toString() || ""; @@ -105,8 +108,8 @@ export async function createTicketAction( const title = prepareEmailTitle( product, problemArea, - account.data.email, - account.data.name, + account.email || "", + account.name || "", ); const keyVal: Record = {}; @@ -117,10 +120,10 @@ export async function createTicketAction( const markdown = prepareEmailBody({ product, markdownInput: keyVal.markdown || "", - email: account.data.email, - name: account.data.name, + email: account.email || "", + name: account.name || "", extraInfoInput: keyVal, - walletAddress: activeAccount, + walletAddress: walletAddress, }); const content = { @@ -129,9 +132,9 @@ export async function createTicketAction( markdown, status: "open", onBehalfOf: { - email: account.data.email, - name: account.data.name, - id: account.data.id, + email: account.email, + name: account.name, + id: account.id, }, customerId, emailInboxId: process.env.UNTHREAD_EMAIL_INBOX_ID, diff --git a/apps/dashboard/src/app/(dashboard)/support/create-ticket/components/create-ticket.client.tsx b/apps/dashboard/src/app/(dashboard)/support/create-ticket/components/create-ticket.client.tsx index d7159771525..800e9bfef14 100644 --- a/apps/dashboard/src/app/(dashboard)/support/create-ticket/components/create-ticket.client.tsx +++ b/apps/dashboard/src/app/(dashboard)/support/create-ticket/components/create-ticket.client.tsx @@ -3,6 +3,7 @@ import { Spinner } from "@/components/ui/Spinner/Spinner"; import { Button } from "@/components/ui/button"; import { Skeleton } from "@/components/ui/skeleton"; +import { cn } from "@/lib/utils"; import { SupportForm_SelectInput } from "components/help/contact-forms/shared/SupportForm_SelectInput"; import dynamic from "next/dynamic"; import { @@ -14,6 +15,7 @@ import { } from "react"; import { useFormStatus } from "react-dom"; import { toast } from "sonner"; +import { SupportForm_TeamSelection } from "../../../../../components/help/contact-forms/shared/SupportForm_TeamSelection"; import { createTicketAction } from "./create-ticket.action"; const ConnectSupportForm = dynamic( @@ -75,14 +77,46 @@ const productOptions: { label: string; component: ReactElement }[] = [ }, ]; -export function CreateTicket() { +function ProductAreaSelection(props: { + productLabel: string; + setProductLabel: (val: string) => void; +}) { + const { productLabel, setProductLabel } = props; + + return ( +
+ o.label)} + promptText="Select a product" + onValueChange={setProductLabel} + value={productLabel} + required={true} + /> + {productOptions.find((o) => o.label === productLabel)?.component} +
+ ); +} + +export function CreateTicket(props: { + teams: { + name: string; + id: string; + }[]; +}) { const formRef = useRef(null); + const [selectedTeamId, setSelectedTeamId] = useState( + props.teams[0]?.id, + ); + const [productLabel, setProductLabel] = useState(""); const [state, formAction] = useActionState(createTicketAction, { message: "", success: false, }); + // needed here // eslint-disable-next-line no-restricted-syntax useEffect(() => { @@ -112,16 +146,19 @@ export function CreateTicket() {
- o.label)} - promptText="Select a product" - onValueChange={setProductLabel} - value={productLabel} - required={true} + {/* Don't conditionally render this - it has be rendered to submit the input values */} +
+ setSelectedTeamId(teamId)} + teams={props.teams} + /> +
+ + - {productOptions.find((o) => o.label === productLabel)?.component}
diff --git a/apps/dashboard/src/app/(dashboard)/support/create-ticket/page.tsx b/apps/dashboard/src/app/(dashboard)/support/create-ticket/page.tsx index 0adec652728..4ae7c561213 100644 --- a/apps/dashboard/src/app/(dashboard)/support/create-ticket/page.tsx +++ b/apps/dashboard/src/app/(dashboard)/support/create-ticket/page.tsx @@ -1,3 +1,4 @@ +import { getTeams } from "@/api/team"; import { Breadcrumb, BreadcrumbItem, @@ -7,17 +8,16 @@ import { BreadcrumbSeparator, } from "@/components/ui/breadcrumb"; import Link from "next/link"; -import { redirect } from "next/navigation"; -import { getAuthToken } from "../../../api/lib/getAuthToken"; +import { loginRedirect } from "../../../login/loginRedirect"; import { CreateTicket } from "./components/create-ticket.client"; export default async function Page() { - const authToken = await getAuthToken(); + const teams = await getTeams(); - if (!authToken) { - return redirect( - `/login?next=${encodeURIComponent("/support/create-ticket")}`, - ); + const pagePath = "/support/create-ticket"; + + if (!teams || teams.length === 0) { + loginRedirect(pagePath); } return ( @@ -36,7 +36,12 @@ export default async function Page() {
- + ({ + name: t.name, + id: t.id, + }))} + />
); diff --git a/apps/dashboard/src/components/help/contact-forms/shared/SupportForm_SelectInput.tsx b/apps/dashboard/src/components/help/contact-forms/shared/SupportForm_SelectInput.tsx index 076cebe6575..2625655e936 100644 --- a/apps/dashboard/src/components/help/contact-forms/shared/SupportForm_SelectInput.tsx +++ b/apps/dashboard/src/components/help/contact-forms/shared/SupportForm_SelectInput.tsx @@ -13,7 +13,7 @@ type Props = { formLabel: string; name: string; required: boolean; - value: string; + value: string | undefined; onValueChange: (value: string) => void; }; @@ -22,14 +22,6 @@ export const SupportForm_SelectInput = (props: Props) => { return ( <> - props.onValueChange(e.target.value)} - required={required} - /> -
{ + props.onChange(selectedId); + }} + > + + + {selectedTeamName} + + + + {props.teams.map((team) => ( + + {team.name} + + ))} + + +
+ + ); +};