From 2967f7eb7e7998fddbefa069f756e35a91d8d095 Mon Sep 17 00:00:00 2001 From: 0xFirekeeper <0xFirekeeper@gmail.com> Date: Thu, 22 Oct 2026 04:12:23 +0700 Subject: [PATCH] dashboard: send product feedback to the team Co-Authored-By: Claude Opus 5.5 --- .../src/@/actions/product-feedback.ts | 39 +++++++++++++++++++ .../Header/SecondaryNav/SecondaryNav.tsx | 20 ++++++++-- .../components/MobileBurgerMenuButton.tsx | 22 +++++++++-- 3 files changed, 75 insertions(+), 6 deletions(-) create mode 100644 apps/dashboard/src/@/actions/product-feedback.ts diff --git a/apps/dashboard/src/@/actions/product-feedback.ts b/apps/dashboard/src/@/actions/product-feedback.ts new file mode 100644 index 00000000000..c12d10b5168 --- /dev/null +++ b/apps/dashboard/src/@/actions/product-feedback.ts @@ -0,0 +1,39 @@ +"use server"; +import "server-only"; + +import { getAuthToken } from "@/api/auth-token"; +import { NEXT_PUBLIC_THIRDWEB_API_HOST } from "@/constants/public-envs"; + +export async function submitProductFeedback(request: { + feedback: string; + source: "desktop" | "mobile"; +}): Promise<{ ok: true } | { ok: false; error: string }> { + const token = await getAuthToken(); + if (!token) { + return { error: "You are not logged in", ok: false }; + } + + const res = await fetch( + new URL("/v1/account/feedback", NEXT_PUBLIC_THIRDWEB_API_HOST), + { + body: JSON.stringify(request), + headers: { + Authorization: `Bearer ${token}`, + "Content-Type": "application/json", + }, + method: "POST", + }, + ); + + if (!res.ok) { + return { + error: + res.status === 429 + ? "Too many requests, please try again in a minute" + : "Failed to submit feedback, please try again later", + ok: false, + }; + } + + return { ok: true }; +} diff --git a/apps/dashboard/src/app/(app)/components/Header/SecondaryNav/SecondaryNav.tsx b/apps/dashboard/src/app/(app)/components/Header/SecondaryNav/SecondaryNav.tsx index c8828651399..e4ae5eacb5f 100644 --- a/apps/dashboard/src/app/(app)/components/Header/SecondaryNav/SecondaryNav.tsx +++ b/apps/dashboard/src/app/(app)/components/Header/SecondaryNav/SecondaryNav.tsx @@ -5,6 +5,7 @@ import type React from "react"; import { useState } from "react"; import { toast } from "sonner"; import type { ThirdwebClient } from "thirdweb"; +import { submitProductFeedback } from "@/actions/product-feedback"; import { reportProductFeedback } from "@/analytics/report"; import { NotificationsButton } from "@/components/notifications/notification-button"; import { NavLink } from "@/components/ui/NavLink"; @@ -39,8 +40,9 @@ export function SecondaryNav(props: { export function SecondaryNavLinks() { const [showFeedbackDropdown, setShowFeedbackDropdown] = useState(false); const [modalFeedback, setModalFeedback] = useState(""); + const [isSubmitting, setIsSubmitting] = useState(false); - const handleModalSubmit = (e?: React.FormEvent) => { + const handleModalSubmit = async (e?: React.FormEvent) => { e?.preventDefault(); // Report feedback to PostHog @@ -49,6 +51,18 @@ export function SecondaryNavLinks() { source: "desktop", }); + setIsSubmitting(true); + const result = await submitProductFeedback({ + feedback: modalFeedback, + source: "desktop", + }); + setIsSubmitting(false); + + if (!result.ok) { + toast.error(result.error); + return; + } + // Show success notification toast.success("Feedback submitted successfully!", { description: "Thank you for your feedback. We'll review it shortly.", @@ -135,8 +149,8 @@ export function SecondaryNavLinks() {