-
Notifications
You must be signed in to change notification settings - Fork 619
Fix/feedback api endpoints #8024
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
|
|
Note Other AI code review bot(s) detectedCodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review. WalkthroughSupportCaseDetails was changed to a locally-declared function with a bottom-level export and gained non-fatal status-check handling, an interactive 5‑star rating, auto-resize textarea, refined submission/error classification, and adjusted reply error handling. apis/feedback.ts added strict input validation, JSON/shape checks, 10s AbortController timeouts, and granular error messages. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
participant UI as SupportCaseDetails UI
participant Q as React-Query Cache
participant API as Support Feedback API
rect #EAF7FF
UI->>API: GET /feedback/status?ticket_id=... (checkFeedbackStatus)
Note right of API: AbortController (10s), parse JSON, validate shape
API-->>UI: { has_feedback: boolean } or error string
end
alt loading
UI->>UI: show "Checking feedback status..." loader
else success & no feedback
UI->>UI: render 5-star rating + AutoResizeTextarea + Send Feedback
else status-check failed (non-fatal)
UI->>UI: show warning + allow feedback submission
end
UI->>API: POST /feedback (submitSupportFeedback {ticketId,rating,feedback})
Note right of API: validate inputs, normalize feedback, 10s timeout, parse JSON
API-->>UI: { success: true } or error string
alt success
UI->>Q: update cache (mark feedback submitted)
UI->>UI: show toast, reset inputs, render Thank you
else error
UI->>UI: classify error (timeout/network/server/validation) and show message
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Pre-merge checks (1 passed, 2 warnings)❌ Failed checks (2 warnings)
✅ Passed checks (1 passed)
Warning Review ran into problems🔥 ProblemsErrors were encountered while retrieving linked issues. Errors (1)
✨ Finishing touches
🧪 Generate unit tests
Comment |
How to use the Graphite Merge QueueAdd either label to this PR to merge it via the merge queue:
You must have a Graphite account in order to use the merge queue. Sign up using this link. An organization admin has enabled the Graphite Merge Queue in this repository. Please do not merge from GitHub as this will restart CI on PRs being processed by the merge queue. |
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #8024 +/- ##
=======================================
Coverage 56.63% 56.63%
=======================================
Files 904 904
Lines 58684 58684
Branches 4162 4162
=======================================
Hits 33233 33233
Misses 25345 25345
Partials 106 106
🚀 New features to boost your workflow:
|
...dashboard/src/app/(app)/team/[team_slug]/(team)/~/support/_components/SupportCaseDetails.tsx
Outdated
Show resolved
Hide resolved
...dashboard/src/app/(app)/team/[team_slug]/(team)/~/support/_components/SupportCaseDetails.tsx
Outdated
Show resolved
Hide resolved
apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/support/apis/feedback.ts
Outdated
Show resolved
Hide resolved
apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/support/apis/feedback.ts
Show resolved
Hide resolved
size-limit report 📦
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 3
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/support/apis/feedback.ts (1)
44-48: Trim ticketId before sending to APITicket ID is validated with trim but the untrimmed value is sent. Send the normalized value to avoid subtle 404s.
const payload = { rating: rating.toString(), feedback: normalizedFeedback, - ticket_id: ticketId, + ticket_id: ticketId.trim(), };
🧹 Nitpick comments (18)
apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/support/apis/feedback.ts (5)
65-68: Cap server-error text to prevent log/PII bloatAPI error bodies can be large. Return a bounded excerpt.
- const errorText = await response.text(); - const error = `API Server error: ${response.status} - ${errorText}`; + const raw = await response.text(); + const errorText = raw.length > 500 ? `${raw.slice(0, 500)}…` : raw; + const error = `API Server error: ${response.status} - ${errorText}`; return { error };
114-129: Drop unnecessary Content-Type on GET and prefer URLSearchParamsNo body is sent; Content-Type is unnecessary. Using URL/URLSearchParams avoids manual encoding.
- const response = await fetch( - `${siwaUrl}/v1/csat/getCSATFeedback?ticket_id=${encodeURIComponent( - ticketId.trim(), - )}`, - { - method: "GET", - cache: "no-store", - headers: { - "Content-Type": "application/json", - "x-service-api-key": apiKey, - }, - signal: ac.signal, - }, - ).finally(() => clearTimeout(t)); + const url = new URL("/v1/csat/getCSATFeedback", siwaUrl); + url.search = new URLSearchParams({ ticket_id: ticketId.trim() }).toString(); + const response = await fetch(url, { + method: "GET", + cache: "no-store", + headers: { "x-service-api-key": apiKey }, + signal: ac.signal, + }).finally(() => clearTimeout(t));
132-134: Cap server-error text for status check as wellMirror the submit path to bound error payload size.
- const errorText = await response.text(); - const error = `API Server error: ${response.status} - ${errorText}`; + const raw = await response.text(); + const errorText = raw.length > 500 ? `${raw.slice(0, 500)}…` : raw; + const error = `API Server error: ${response.status} - ${errorText}`; return { error };
50-53: Optional: simplify timeouts with AbortSignal.timeoutIf runtime is Node 18.17+/Next 14+, AbortSignal.timeout can replace the manual controller/clearTimeout.
- const ac = new AbortController(); - const t = setTimeout(() => ac.abort(), 10_000); + // Node 18.17+/Next 14+ + const signal = AbortSignal.timeout(10_000); @@ - signal: ac.signal, - }).finally(() => clearTimeout(t)); + signal, + });Confirm your deployed runtime supports AbortSignal.timeout. If not, keep the current approach.
17-28: Consider avoiding NEXT_PUBLIC_ fallback on the serverUsing NEXT_PUBLIC_* on server can mask missing server config and risks leaking base URLs to clients elsewhere.
Extract SIWA config once and fail fast on missing SIWA_URL; keep NEXT_PUBLIC_* only for client code paths.
Also applies to: 95-106
apps/dashboard/src/@/analytics/report.ts (4)
564-571: Add explicit return type : voidKeep consistency with “explicit return types” guideline.
-export function reportSupportFeedbackSubmitted(properties: { +export function reportSupportFeedbackSubmitted(properties: { ticketId: string; rating: number; hasFeedback: boolean; feedbackLength?: number; -}) { +}): void { posthog.capture("support feedback submitted", properties); }
582-591: Add explicit return type : void-export function reportSupportFeedbackFailed(properties: { +export function reportSupportFeedbackFailed(properties: { ticketId: string; rating: number; hasFeedback: boolean; feedbackLength?: number; errorMessage: string; errorType: "validation" | "network" | "server" | "unknown"; -}) { +}): void { posthog.capture("support feedback failed", properties); }
602-608: Add explicit return type : void-export function reportSupportFeedbackStatusCheckFailed(properties: { +export function reportSupportFeedbackStatusCheckFailed(properties: { ticketId: string; errorMessage: string; errorType: "network" | "server" | "unknown"; -}) { +}): void { posthog.capture("support feedback status check failed", properties); }
551-609: Optional: unify error type(s) for feedback analyticsCreate a shared type alias to avoid drift across events.
// near the SUPPORT FEEDBACK section header type SupportFeedbackErrorType = "validation" | "network" | "server" | "unknown";Then reference it in both helpers.
apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/support/cases/[id]/page.tsx (1)
1-1: Optional: mark this page as server-onlyAdds guardrail against accidental client import.
import "server-only";apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/support/_components/SupportCaseDetails.tsx (8)
42-42: Add explicit return types to componentsAlign with our TS guidelines by annotating component return types.
-function SupportCaseDetails({ ticket, team }: SupportCaseDetailsProps) { +function SupportCaseDetails({ ticket, team }: SupportCaseDetailsProps): JSX.Element { @@ -function TicketHeader(props: { +function TicketHeader(props: { title: string; createdAt: string; updatedAt: string; status: SupportTicket["status"]; -}) { +}): JSX.Element { @@ -function TicketMessage(props: { message: SupportMessage }) { +function TicketMessage(props: { message: SupportMessage }): JSX.Element { @@ -export function SupportCaseDetailsWithErrorBoundary( - props: SupportCaseDetailsProps, -) { +export function SupportCaseDetailsWithErrorBoundary( + props: SupportCaseDetailsProps, +): JSX.Element {Also applies to: 618-623, 654-654, 790-799
37-40: Prefertypealias overinterfacefor propsProject convention: use
typefor structural props.-interface SupportCaseDetailsProps { - ticket: SupportTicket; - team: Team; -} +type SupportCaseDetailsProps = { + ticket: SupportTicket; + team: Team; +};
188-200: Clarify log context naming:hasFeedbackhere means “has text comment”, not “already submitted”This collides with the “feedback already submitted” concept from the status check. Rename in logs to avoid confusion while keeping analytics payloads unchanged.
- hasFeedback: Boolean(feedback?.trim()), + hasTextFeedback: Boolean(feedback?.trim()), @@ - hasFeedback: Boolean(feedback?.trim()), + hasTextFeedback: Boolean(feedback?.trim()), @@ - hasFeedback: Boolean(feedback?.trim()), + hasTextFeedback: Boolean(feedback?.trim()), @@ - hasFeedback: Boolean(feedback?.trim()), + hasTextFeedback: Boolean(feedback?.trim()),Also applies to: 267-274, 317-324, 366-379
78-86: Factor repeated error-type classification into a helperReduces duplication and drift across paths.
+function classifyErrorType(msg: string): + | "validation" + | "network" + | "server" + | "unknown" { + if (/validation|Rating must be|Please select a rating/i.test(msg)) return "validation"; + if (/network|fetch/i.test(msg)) return "network"; + if (/API Server error|Internal server error/i.test(msg)) return "server"; + return "unknown"; +} @@ - const errorType = /network|fetch/i.test(result.error) - ? "network" - : /API Server error/i.test(result.error) - ? "server" - : /Internal server error/i.test(result.error) - ? "server" - : "unknown"; + const errorType = classifyErrorType(result.error); @@ - const errorType = /network|fetch/i.test( - error instanceof Error ? error.message : String(error), - ) - ? "network" - : /API Server error/i.test( - error instanceof Error ? error.message : String(error), - ) - ? "server" - : "unknown"; + const errorType = classifyErrorType( + error instanceof Error ? error.message : String(error), + ); @@ - let errorType: "validation" | "network" | "server" | "unknown" = - "unknown"; + let errorType: "validation" | "network" | "server" | "unknown" = "unknown"; @@ - } else if (/API Server error/i.test(msg)) { + } else if (/API Server error/i.test(msg)) { message = "Server error. Please try again later."; - errorType = "server"; + errorType = "server"; } @@ - reportSupportFeedbackFailed({ + reportSupportFeedbackFailed({ ticketId: this.props.ticketId, rating: 0, hasFeedback: false, feedbackLength: 0, - errorMessage: `Error boundary: ${error.message}`, - errorType: "unknown", + errorMessage: `Error boundary: ${error.message}`, + errorType: classifyErrorType(error.message), });Also applies to: 142-152, 339-350, 760-767
202-206: Gate verbose console logging behind a debug flagKeep production console clean and reduce noise. Example pattern:
+const DEBUG_FEEDBACK = process.env.NEXT_PUBLIC_FEEDBACK_DEBUG === "true"; @@ - console.log( + DEBUG_FEEDBACK && console.log( "[SUPPORT_FEEDBACK_CLIENT] Starting feedback submission", logContext, ); @@ - console.error( + (DEBUG_FEEDBACK ? console.error : console.log)( "[SUPPORT_FEEDBACK_CLIENT] Feedback submission failed", { ...logContext, error: result.error, errorType: typeof result.error, errorLength: result.error?.length || 0, }, ); @@ - console.log( + DEBUG_FEEDBACK && console.log( "[SUPPORT_FEEDBACK_CLIENT] Feedback submission successful", logContext, ); @@ - console.log( + DEBUG_FEEDBACK && console.log( "[SUPPORT_FEEDBACK_CLIENT] Feedback submission success callback", logContext, ); @@ - console.log("[SUPPORT_FEEDBACK_CLIENT] Success state updates starting", { + DEBUG_FEEDBACK && console.log("[SUPPORT_FEEDBACK_CLIENT] Success state updates starting", { ...logContext, currentRating: rating, currentFeedback: feedback, queryDataBefore: queryClient.getQueryData([ "feedbackStatus", ticket.id, ]), }); @@ - console.log("[SUPPORT_FEEDBACK_CLIENT] Success state updates completed", { + DEBUG_FEEDBACK && console.log("[SUPPORT_FEEDBACK_CLIENT] Success state updates completed", { ...logContext, queryDataAfter: queryClient.getQueryData(["feedbackStatus", ticket.id]), ratingAfter: 0, feedbackAfter: "", }); @@ - console.error( + (DEBUG_FEEDBACK ? console.error : console.log)( "[SUPPORT_FEEDBACK_CLIENT] Feedback submission error callback", { ...logContext, error: err instanceof Error ? err.message : String(err), errorName: err instanceof Error ? err.name : "Unknown", }, ); @@ - console.log( + DEBUG_FEEDBACK && console.log( "[SUPPORT_FEEDBACK_CLIENT] User initiated feedback submission", logContext, ); @@ - console.error("[SUPPORT_FEEDBACK_CLIENT] Error boundary caught error:", { + (DEBUG_FEEDBACK ? console.error : console.log)("[SUPPORT_FEEDBACK_CLIENT] Error boundary caught error:", { ticketId: this.props.ticketId, error: error.message, errorStack: error.stack, errorInfo, timestamp: new Date().toISOString(), });Also applies to: 225-233, 252-256, 275-279, 291-315, 325-333, 381-384, 750-758
459-466: ExposeclassNameon root for composability (dashboard guideline)Add an optional
classNameprop and merge viacn()on the root container.-type SupportCaseDetailsProps = { - ticket: SupportTicket; - team: Team; -}; +type SupportCaseDetailsProps = { + ticket: SupportTicket; + team: Team; + className?: string; +}; @@ -function SupportCaseDetails({ ticket, team }: SupportCaseDetailsProps): JSX.Element { +function SupportCaseDetails({ ticket, team, className }: SupportCaseDetailsProps): JSX.Element { @@ - return ( - <div className="flex flex-col grow"> + return ( + <div className={cn("flex flex-col grow", className)}>
705-716: Type the keyboard event targetUse the textarea-specific event type for better intellisense and safety.
-const handleKeyDown = (e: React.KeyboardEvent) => { +const handleKeyDown = (e: React.KeyboardEvent<HTMLTextAreaElement>) => {
750-757: Use React’sErrorInfotype forcomponentDidCatchMinor typing polish.
- componentDidCatch(error: Error, errorInfo: { componentStack: string }) { + componentDidCatch(error: Error, errorInfo: React.ErrorInfo) {
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (4)
apps/dashboard/src/@/analytics/report.ts(1 hunks)apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/support/_components/SupportCaseDetails.tsx(6 hunks)apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/support/apis/feedback.ts(3 hunks)apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/support/cases/[id]/page.tsx(2 hunks)
🧰 Additional context used
📓 Path-based instructions (6)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.{ts,tsx}: Write idiomatic TypeScript with explicit function declarations and return types
Limit each file to one stateless, single-responsibility function for clarity
Re-use shared types from@/typesor localtypes.tsbarrels
Prefer type aliases over interface except for nominal shapes
Avoidanyandunknownunless unavoidable; narrow generics when possible
Choose composition over inheritance; leverage utility types (Partial,Pick, etc.)
Comment only ambiguous logic; avoid restating TypeScript in prose
**/*.{ts,tsx}: Use explicit function declarations and explicit return types in TypeScript
Limit each file to one stateless, single‑responsibility function
Re‑use shared types from@/typeswhere applicable
Prefertypealiases overinterfaceexcept for nominal shapes
Avoidanyandunknownunless unavoidable; narrow generics when possible
Prefer composition over inheritance; use utility types (Partial, Pick, etc.)
Lazy‑import optional features and avoid top‑level side‑effects to reduce bundle size
Files:
apps/dashboard/src/@/analytics/report.tsapps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/support/_components/SupportCaseDetails.tsxapps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/support/apis/feedback.tsapps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/support/cases/[id]/page.tsx
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (CLAUDE.md)
Load heavy dependencies inside async paths to keep initial bundle lean (lazy loading)
Files:
apps/dashboard/src/@/analytics/report.tsapps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/support/_components/SupportCaseDetails.tsxapps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/support/apis/feedback.tsapps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/support/cases/[id]/page.tsx
apps/{dashboard,playground-web}/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
apps/{dashboard,playground-web}/**/*.{ts,tsx}: Import UI primitives from@/components/ui/*(Button, Input, Select, Tabs, Card, Sidebar, Badge, Separator) in dashboard and playground apps
UseNavLinkfor internal navigation with automatic active states in dashboard and playground apps
Use Tailwind CSS only – no inline styles or CSS modules
Usecn()from@/lib/utilsfor conditional class logic
Use design system tokens (e.g.,bg-card,border-border,text-muted-foreground)
Server Components (Node edge): Start files withimport "server-only";
Client Components (browser): Begin files with'use client';
Always callgetAuthToken()to retrieve JWT from cookies on server side
UseAuthorization: Bearerheader – never embed tokens in URLs
Return typed results (e.g.,Project[],User[]) – avoidany
Wrap client-side data fetching calls in React Query (@tanstack/react-query)
Use descriptive, stablequeryKeysfor React Query cache hits
ConfigurestaleTime/cacheTimein React Query based on freshness (default ≥ 60s)
Keep tokens secret via internal API routes or server actions
Never importposthog-jsin server components
Files:
apps/dashboard/src/@/analytics/report.tsapps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/support/_components/SupportCaseDetails.tsxapps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/support/apis/feedback.tsapps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/support/cases/[id]/page.tsx
apps/{dashboard,playground}/**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
apps/{dashboard,playground}/**/*.{ts,tsx}: Import UI primitives from@/components/ui/_(e.g., Button, Input, Tabs, Card)
UseNavLinkfor internal navigation to get active state handling
Use Tailwind CSS for styling; no inline styles
Merge class names withcn()from@/lib/utilsfor conditional classes
Stick to design tokens (e.g., bg-card, border-border, text-muted-foreground)
Server Components must start withimport "server-only"; usenext/headers, server‑only env, heavy data fetching, andredirect()where appropriate
Client Components must start with'use client'; handle interactivity with hooks and browser APIs
Server-side data fetching: callgetAuthToken()from cookies, sendAuthorization: Bearer <token>header, and return typed results (avoidany)
Client-side data fetching: wrap calls in React Query with descriptive, stablequeryKeysand set sensiblestaleTime/cacheTime(≥ 60s default); keep tokens secret via internal routes or server actions
Do not importposthog-jsin server components (client-side only)
Files:
apps/dashboard/src/@/analytics/report.tsapps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/support/_components/SupportCaseDetails.tsxapps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/support/apis/feedback.tsapps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/support/cases/[id]/page.tsx
apps/{dashboard,playground}/src/@/analytics/report.ts
📄 CodeRabbit inference engine (AGENTS.md)
apps/{dashboard,playground}/src/@/analytics/report.ts: Checkreport.tsbefore adding a new analytics event to avoid duplicates
Analytics naming: event name as<subject> <verb>; helper function asreport<Subject><Verb>(PascalCase)
Each analytics helper must include a JSDoc header (Why/Owner), accept a single typedpropertiesobject, and forward it unchanged toposthog.capture
Files:
apps/dashboard/src/@/analytics/report.ts
apps/{dashboard,playground}/**/*.tsx
📄 CodeRabbit inference engine (AGENTS.md)
Expose a
classNameprop on the root element of every component
Files:
apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/support/_components/SupportCaseDetails.tsxapps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/support/cases/[id]/page.tsx
🧠 Learnings (6)
📚 Learning: 2025-08-29T15:37:38.513Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: AGENTS.md:0-0
Timestamp: 2025-08-29T15:37:38.513Z
Learning: Applies to apps/{dashboard,playground}/src/@/analytics/report.ts : Analytics naming: event name as `<subject> <verb>`; helper function as `report<Subject><Verb>` (PascalCase)
Applied to files:
apps/dashboard/src/@/analytics/report.ts
📚 Learning: 2025-07-18T19:19:55.613Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: CLAUDE.md:0-0
Timestamp: 2025-07-18T19:19:55.613Z
Learning: Applies to src/@/analytics/report.ts : Review `src/@/analytics/report.ts` before adding analytics events to check for duplicates
Applied to files:
apps/dashboard/src/@/analytics/report.ts
📚 Learning: 2025-07-18T19:20:32.530Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: .cursor/rules/dashboard.mdc:0-0
Timestamp: 2025-07-18T19:20:32.530Z
Learning: Applies to dashboard/src/@/analytics/report.ts : Reporting helper: `report<Subject><Verb>` (PascalCase); all live in `src/@/analytics/report.ts`.
Applied to files:
apps/dashboard/src/@/analytics/report.ts
📚 Learning: 2025-08-29T15:37:38.513Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: AGENTS.md:0-0
Timestamp: 2025-08-29T15:37:38.513Z
Learning: Applies to apps/{dashboard,playground}/src/@/analytics/report.ts : Check `report.ts` before adding a new analytics event to avoid duplicates
Applied to files:
apps/dashboard/src/@/analytics/report.ts
📚 Learning: 2025-07-18T19:19:55.613Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: CLAUDE.md:0-0
Timestamp: 2025-07-18T19:19:55.613Z
Learning: Applies to src/@/analytics/report.ts : Analytics event name: human-readable `<subject> <verb>` (e.g., "contract deployed"); function: `report<Subject><Verb>` (PascalCase)
Applied to files:
apps/dashboard/src/@/analytics/report.ts
📚 Learning: 2025-07-18T19:20:32.530Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: .cursor/rules/dashboard.mdc:0-0
Timestamp: 2025-07-18T19:20:32.530Z
Learning: Applies to dashboard/**/*.{tsx,jsx} : Icons come from `lucide-react` or the project-specific `…/icons` exports – never embed raw SVG.
Applied to files:
apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/support/_components/SupportCaseDetails.tsx
🧬 Code graph analysis (2)
apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/support/_components/SupportCaseDetails.tsx (2)
apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/support/apis/feedback.ts (2)
checkFeedbackStatus(91-170)submitSupportFeedback(6-89)apps/dashboard/src/@/analytics/report.ts (3)
reportSupportFeedbackStatusCheckFailed(602-608)reportSupportFeedbackSubmitted(564-571)reportSupportFeedbackFailed(582-591)
apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/support/cases/[id]/page.tsx (1)
apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/support/_components/SupportCaseDetails.tsx (1)
SupportCaseDetailsWithErrorBoundary(791-799)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
- GitHub Check: Lint Packages
- GitHub Check: Size
- GitHub Check: Analyze (javascript)
🔇 Additional comments (8)
apps/dashboard/src/@/analytics/report.ts (2)
551-609: Events follow naming conventions and property pass-throughEvent names and helper names match guidelines; JSDoc present; properties are forwarded unchanged.
1-609: All support feedback capture calls are unique
No duplicate event names found.apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/support/cases/[id]/page.tsx (3)
5-5: Good swap to error-boundary wrapperImporting SupportCaseDetailsWithErrorBoundary aligns with the new public surface and isolates runtime errors.
37-42: Wrapper usage looks correctProps preserved; server page rendering a client wrapper is fine.
5-5: No remainingSupportCaseDetailsimports — confirmed no other files import the oldSupportCaseDetailssymbol.apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/support/_components/SupportCaseDetails.tsx (3)
172-175: Good use of React Query v5 options
staleTimeandgcTimeare set sensibly; key is stable. LGTM.
5-5: Icon source complianceUsing
lucide-reactand the project icon component aligns with our icon guideline. 👍Also applies to: 684-690
280-287: Analytics field semantics: confirm intent ofhasFeedbackYou send
hasFeedback: Boolean(feedback?.trim())in analytics. Confirm downstream expects “has text comment” vs. “ticket already had CSAT”. If it’s the latter, consider sending an extra flag or renaming.Do you want me to generate a quick PR to add a separate
hadExistingFeedbackboolean to the analytics payloads and reports?Also applies to: 352-360
...dashboard/src/app/(app)/team/[team_slug]/(team)/~/support/_components/SupportCaseDetails.tsx
Outdated
Show resolved
Hide resolved
apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/support/apis/feedback.ts
Show resolved
Hide resolved
apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/support/apis/feedback.ts
Show resolved
Hide resolved
...dashboard/src/app/(app)/team/[team_slug]/(team)/~/support/_components/SupportCaseDetails.tsx
Outdated
Show resolved
Hide resolved
...dashboard/src/app/(app)/team/[team_slug]/(team)/~/support/_components/SupportCaseDetails.tsx
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
♻️ Duplicate comments (3)
apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/support/apis/feedback.ts (3)
16-24: Good: fail-fast input validation is now firstMoving ticketId/rating validation before config/network work addresses the earlier concern and reduces wasted work.
78-83: Correct network error detectionUsing AbortError and TypeError branches is the right way to classify fetch failures.
170-175: Same here: error classification is correctMirrors the submit path; good consistency.
🧹 Nitpick comments (13)
apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/support/apis/feedback.ts (3)
39-48: Trim ticketId before sending; keep payload normalizedMinor consistency fix: you validate with trim but send the raw ticketId. Also, only stringify rating if the API requires it.
Apply:
const payload = { - rating: rating.toString(), + // Keep as number unless the API strictly requires a string + rating: rating.toString(), feedback: normalizedFeedback, - ticket_id: ticketId, + ticket_id: ticketId.trim(), };If SIWA accepts numeric rating, switch to
rating(number) here and in analytics to avoid type drift. Do you want me to adjust both sides?
70-75: Guard JSON parsing for submit path (symmetry with status path)If the API ever returns non-JSON (e.g., empty 204), this throws and returns a cryptic message. Mirror the status handler’s parse guard.
- const data = await response.json(); + let data: { success?: boolean } = {}; + try { + data = await response.json(); + } catch (_jsonErr) { + return { error: "Invalid JSON response from API" }; + }
113-126: Nit: remove Content-Type for GET; add Accept on both callsContent-Type on GET is unnecessary. Prefer advertising desired response via Accept.
headers: { - "Content-Type": "application/json", + Accept: "application/json", "x-service-api-key": apiKey, },And for the POST headers:
headers: { "Content-Type": "application/json", + Accept: "application/json", "x-service-api-key": apiKey, },apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/support/_components/SupportCaseDetails.tsx (10)
53-89: Unify error classification and remove no-op return from a throwing helper
- Classify network via TypeError (not message regex) to match fetch semantics.
handleStatusCheckErroralways throws; returning its value is misleading.const handleStatusCheckError = ( error: unknown, logContext: Record<string, string | number | boolean>, ) => { - const errorMessage = error instanceof Error ? error.message : String(error); - const errorType = /network|fetch/i.test(errorMessage) - ? "network" - : /API Server error/i.test(errorMessage) - ? "server" - : /Internal server error/i.test(errorMessage) - ? "server" - : "unknown"; + const isTypeErr = error instanceof TypeError; + const errorMessage = error instanceof Error ? error.message : String(error); + const errorType = isTypeErr + ? "network" + : /API Server error|Internal server error/i.test(errorMessage) + ? "server" + : "unknown"; @@ - // Throw a custom error that React Query will catch, but the UI will handle gracefully - // This prevents treating the error as "no feedback exists" which could cause duplicates + // Throw: React Query will surface isError; UI remains unblocked throw new Error(`Status check failed: ${errorMessage}`); };And where you use it:
- // Use shared error handler to maintain consistent behavior - return handleStatusCheckError(result.error, logContext); + // Use shared error handler to maintain consistent behavior + handleStatusCheckError(result.error, logContext);- // Use shared error handler to maintain consistent behavior - return handleStatusCheckError(error, logContext); + // Use shared error handler to maintain consistent behavior + handleStatusCheckError(error, logContext);
92-168: Avoid retries to prevent repeated analytics spam on status failuresReact Query retries queries by default; since you intentionally throw to mark degraded state, disable retries.
const feedbackStatusQuery = useQuery({ queryKey: ["feedbackStatus", ticket.id], @@ - staleTime: 60_000, - gcTime: 5 * 60_000, + staleTime: 60_000, + gcTime: 5 * 60_000, + retry: false, });
170-174: Align comment with behavior; rely on isErrorThe query does throw on failures now. Use isError (optionally OR with the local flag if you keep it).
-// query never throws; use local degraded flag for the inline warning -const hasError = statusCheckFailed; +// query throws on failures; surface the inline warning +const hasError = feedbackStatusQuery.isError || statusCheckFailed;
328-345: Classify network errors via TypeError, not message textThis keeps client-side classification consistent with the server helpers.
- const msg = err instanceof Error ? err.message : String(err ?? ""); + const msg = err instanceof Error ? err.message : String(err ?? ""); let message = "Failed to submit feedback. Please try again."; let errorType: "validation" | "network" | "server" | "unknown" = "unknown"; - if (/network|fetch/i.test(msg)) { + if (err instanceof TypeError || /network|fetch/i.test(msg)) { message = "Network error. Please check your connection and try again."; errorType = "network"; } else if (
37-42: Prefer type alias and add a className prop (guideline: expose className in dashboard components)Keeps to the repo style and improves composability.
-interface SupportCaseDetailsProps { - ticket: SupportTicket; - team: Team; -} +type SupportCaseDetailsProps = { + ticket: SupportTicket; + team: Team; + className?: string; +};
42-42: Add explicit return type to componentGuideline: explicit function declarations and return types.
-function SupportCaseDetails({ ticket, team }: SupportCaseDetailsProps) { +function SupportCaseDetails({ ticket, team, className }: SupportCaseDetailsProps): JSX.Element {
455-456: Plumb className onto the root elementExpose styling control to callers.
- return ( - <div className="flex flex-col grow"> + return ( + <div className={cn("flex flex-col grow", className)}>
614-648: Add explicit return type on TicketHeader-function TicketHeader(props: { +function TicketHeader(props: { title: string; createdAt: string; updatedAt: string; status: SupportTicket["status"]; -}) { +}): JSX.Element {
650-731: Add explicit return type on TicketMessage-function TicketMessage(props: { message: SupportMessage }) { +function TicketMessage(props: { message: SupportMessage }): JSX.Element {
787-795: Add explicit return type on the exported wrapper-export function SupportCaseDetailsWithErrorBoundary( - props: SupportCaseDetailsProps, -) { +export function SupportCaseDetailsWithErrorBoundary( + props: SupportCaseDetailsProps, +): JSX.Element {
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (2)
apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/support/_components/SupportCaseDetails.tsx(6 hunks)apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/support/apis/feedback.ts(4 hunks)
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.{ts,tsx}: Write idiomatic TypeScript with explicit function declarations and return types
Limit each file to one stateless, single-responsibility function for clarity
Re-use shared types from@/typesor localtypes.tsbarrels
Prefer type aliases over interface except for nominal shapes
Avoidanyandunknownunless unavoidable; narrow generics when possible
Choose composition over inheritance; leverage utility types (Partial,Pick, etc.)
Comment only ambiguous logic; avoid restating TypeScript in prose
**/*.{ts,tsx}: Use explicit function declarations and explicit return types in TypeScript
Limit each file to one stateless, single‑responsibility function
Re‑use shared types from@/typeswhere applicable
Prefertypealiases overinterfaceexcept for nominal shapes
Avoidanyandunknownunless unavoidable; narrow generics when possible
Prefer composition over inheritance; use utility types (Partial, Pick, etc.)
Lazy‑import optional features and avoid top‑level side‑effects to reduce bundle size
Files:
apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/support/apis/feedback.tsapps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/support/_components/SupportCaseDetails.tsx
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (CLAUDE.md)
Load heavy dependencies inside async paths to keep initial bundle lean (lazy loading)
Files:
apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/support/apis/feedback.tsapps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/support/_components/SupportCaseDetails.tsx
apps/{dashboard,playground-web}/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
apps/{dashboard,playground-web}/**/*.{ts,tsx}: Import UI primitives from@/components/ui/*(Button, Input, Select, Tabs, Card, Sidebar, Badge, Separator) in dashboard and playground apps
UseNavLinkfor internal navigation with automatic active states in dashboard and playground apps
Use Tailwind CSS only – no inline styles or CSS modules
Usecn()from@/lib/utilsfor conditional class logic
Use design system tokens (e.g.,bg-card,border-border,text-muted-foreground)
Server Components (Node edge): Start files withimport "server-only";
Client Components (browser): Begin files with'use client';
Always callgetAuthToken()to retrieve JWT from cookies on server side
UseAuthorization: Bearerheader – never embed tokens in URLs
Return typed results (e.g.,Project[],User[]) – avoidany
Wrap client-side data fetching calls in React Query (@tanstack/react-query)
Use descriptive, stablequeryKeysfor React Query cache hits
ConfigurestaleTime/cacheTimein React Query based on freshness (default ≥ 60s)
Keep tokens secret via internal API routes or server actions
Never importposthog-jsin server components
Files:
apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/support/apis/feedback.tsapps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/support/_components/SupportCaseDetails.tsx
apps/{dashboard,playground}/**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
apps/{dashboard,playground}/**/*.{ts,tsx}: Import UI primitives from@/components/ui/_(e.g., Button, Input, Tabs, Card)
UseNavLinkfor internal navigation to get active state handling
Use Tailwind CSS for styling; no inline styles
Merge class names withcn()from@/lib/utilsfor conditional classes
Stick to design tokens (e.g., bg-card, border-border, text-muted-foreground)
Server Components must start withimport "server-only"; usenext/headers, server‑only env, heavy data fetching, andredirect()where appropriate
Client Components must start with'use client'; handle interactivity with hooks and browser APIs
Server-side data fetching: callgetAuthToken()from cookies, sendAuthorization: Bearer <token>header, and return typed results (avoidany)
Client-side data fetching: wrap calls in React Query with descriptive, stablequeryKeysand set sensiblestaleTime/cacheTime(≥ 60s default); keep tokens secret via internal routes or server actions
Do not importposthog-jsin server components (client-side only)
Files:
apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/support/apis/feedback.tsapps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/support/_components/SupportCaseDetails.tsx
apps/{dashboard,playground}/**/*.tsx
📄 CodeRabbit inference engine (AGENTS.md)
Expose a
classNameprop on the root element of every component
Files:
apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/support/_components/SupportCaseDetails.tsx
🧠 Learnings (1)
📚 Learning: 2025-07-18T19:20:32.530Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: .cursor/rules/dashboard.mdc:0-0
Timestamp: 2025-07-18T19:20:32.530Z
Learning: Applies to dashboard/**/*.{tsx,jsx} : Icons come from `lucide-react` or the project-specific `…/icons` exports – never embed raw SVG.
Applied to files:
apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/support/_components/SupportCaseDetails.tsx
🧬 Code graph analysis (1)
apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/support/_components/SupportCaseDetails.tsx (4)
packages/nebula/src/client/sdk.gen.ts (1)
feedback(377-398)apps/dashboard/src/@/analytics/report.ts (3)
reportSupportFeedbackStatusCheckFailed(602-608)reportSupportFeedbackSubmitted(564-571)reportSupportFeedbackFailed(582-591)apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/support/apis/feedback.ts (2)
checkFeedbackStatus(87-177)submitSupportFeedback(6-85)apps/portal/src/components/ui/textarea.tsx (1)
AutoResizeTextarea(26-48)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (7)
- GitHub Check: E2E Tests (pnpm, esbuild)
- GitHub Check: E2E Tests (pnpm, webpack)
- GitHub Check: E2E Tests (pnpm, vite)
- GitHub Check: Size
- GitHub Check: Lint Packages
- GitHub Check: Unit Tests
- GitHub Check: Analyze (javascript)
🔇 Additional comments (3)
apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/support/apis/feedback.ts (2)
50-63: Timeout/Abort wiring looks solidAbortController + finally clear is correct and prevents leaks.
151-166: Nice: comprehensive runtime shape validationThe explicit checks prevent false type safety from TS declarations.
apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/support/_components/SupportCaseDetails.tsx (1)
505-574: UI flow reads well and meets design tokens; stars + textarea a11y is solidNice accessible labels, disabled states, and token usage.
...dashboard/src/app/(app)/team/[team_slug]/(team)/~/support/_components/SupportCaseDetails.tsx
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🧹 Nitpick comments (1)
apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/support/_components/SupportCaseDetails.tsx (1)
541-584: Add better error recovery UIThe error boundary currently shows a generic error message. Consider providing recovery options or a way for users to retry the feedback submission.
render() { if (this.state.hasError) { return ( <div className="border-t p-6"> - <div className="text-destructive text-sm"> - <p className="font-medium"> - Something went wrong with the feedback system. - </p> - <p className="text-xs mt-1"> - Error: {this.state.error?.message || "Unknown error"} - </p> - </div> + <div className="text-destructive text-sm space-y-3"> + <div> + <p className="font-medium"> + Something went wrong with the feedback system. + </p> + <p className="text-xs mt-1"> + Error: {this.state.error?.message || "Unknown error"} + </p> + </div> + <Button + variant="outline" + size="sm" + onClick={() => this.setState({ hasError: false, error: undefined })} + > + Try Again + </Button> + </div> </div> ); } return this.props.children; }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (1)
apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/support/_components/SupportCaseDetails.tsx(2 hunks)
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.{ts,tsx}: Write idiomatic TypeScript with explicit function declarations and return types
Limit each file to one stateless, single-responsibility function for clarity
Re-use shared types from@/typesor localtypes.tsbarrels
Prefer type aliases over interface except for nominal shapes
Avoidanyandunknownunless unavoidable; narrow generics when possible
Choose composition over inheritance; leverage utility types (Partial,Pick, etc.)
Comment only ambiguous logic; avoid restating TypeScript in prose
**/*.{ts,tsx}: Use explicit function declarations and explicit return types in TypeScript
Limit each file to one stateless, single‑responsibility function
Re‑use shared types from@/typeswhere applicable
Prefertypealiases overinterfaceexcept for nominal shapes
Avoidanyandunknownunless unavoidable; narrow generics when possible
Prefer composition over inheritance; use utility types (Partial, Pick, etc.)
Lazy‑import optional features and avoid top‑level side‑effects to reduce bundle size
Files:
apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/support/_components/SupportCaseDetails.tsx
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (CLAUDE.md)
Load heavy dependencies inside async paths to keep initial bundle lean (lazy loading)
Files:
apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/support/_components/SupportCaseDetails.tsx
apps/{dashboard,playground-web}/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
apps/{dashboard,playground-web}/**/*.{ts,tsx}: Import UI primitives from@/components/ui/*(Button, Input, Select, Tabs, Card, Sidebar, Badge, Separator) in dashboard and playground apps
UseNavLinkfor internal navigation with automatic active states in dashboard and playground apps
Use Tailwind CSS only – no inline styles or CSS modules
Usecn()from@/lib/utilsfor conditional class logic
Use design system tokens (e.g.,bg-card,border-border,text-muted-foreground)
Server Components (Node edge): Start files withimport "server-only";
Client Components (browser): Begin files with'use client';
Always callgetAuthToken()to retrieve JWT from cookies on server side
UseAuthorization: Bearerheader – never embed tokens in URLs
Return typed results (e.g.,Project[],User[]) – avoidany
Wrap client-side data fetching calls in React Query (@tanstack/react-query)
Use descriptive, stablequeryKeysfor React Query cache hits
ConfigurestaleTime/cacheTimein React Query based on freshness (default ≥ 60s)
Keep tokens secret via internal API routes or server actions
Never importposthog-jsin server components
Files:
apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/support/_components/SupportCaseDetails.tsx
apps/{dashboard,playground}/**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
apps/{dashboard,playground}/**/*.{ts,tsx}: Import UI primitives from@/components/ui/_(e.g., Button, Input, Tabs, Card)
UseNavLinkfor internal navigation to get active state handling
Use Tailwind CSS for styling; no inline styles
Merge class names withcn()from@/lib/utilsfor conditional classes
Stick to design tokens (e.g., bg-card, border-border, text-muted-foreground)
Server Components must start withimport "server-only"; usenext/headers, server‑only env, heavy data fetching, andredirect()where appropriate
Client Components must start with'use client'; handle interactivity with hooks and browser APIs
Server-side data fetching: callgetAuthToken()from cookies, sendAuthorization: Bearer <token>header, and return typed results (avoidany)
Client-side data fetching: wrap calls in React Query with descriptive, stablequeryKeysand set sensiblestaleTime/cacheTime(≥ 60s default); keep tokens secret via internal routes or server actions
Do not importposthog-jsin server components (client-side only)
Files:
apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/support/_components/SupportCaseDetails.tsx
apps/{dashboard,playground}/**/*.tsx
📄 CodeRabbit inference engine (AGENTS.md)
Expose a
classNameprop on the root element of every component
Files:
apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/support/_components/SupportCaseDetails.tsx
🧠 Learnings (5)
📓 Common learnings
Learnt from: CR
PR: thirdweb-dev/js#0
File: CLAUDE.md:0-0
Timestamp: 2025-07-18T19:19:55.613Z
Learning: Applies to src/@/analytics/report.ts : Review `src/@/analytics/report.ts` before adding analytics events to check for duplicates
📚 Learning: 2025-05-26T16:31:02.480Z
Learnt from: MananTank
PR: thirdweb-dev/js#7152
File: apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/claim-conditions/shared-claim-conditions-page.tsx:43-49
Timestamp: 2025-05-26T16:31:02.480Z
Learning: In the thirdweb dashboard codebase, when `redirectToContractLandingPage()` is called, an explicit return statement is not required afterward because the function internally calls Next.js's `redirect()` which throws an error to halt execution.
Applied to files:
apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/support/_components/SupportCaseDetails.tsx
📚 Learning: 2025-07-18T19:20:32.530Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: .cursor/rules/dashboard.mdc:0-0
Timestamp: 2025-07-18T19:20:32.530Z
Learning: Applies to dashboard/**/*.{tsx,jsx} : Prefer composable primitives over custom markup: `Button`, `Input`, `Select`, `Tabs`, `Card`, `Sidebar`, `Separator`, `Badge`.
Applied to files:
apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/support/_components/SupportCaseDetails.tsx
📚 Learning: 2025-07-18T19:20:32.530Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: .cursor/rules/dashboard.mdc:0-0
Timestamp: 2025-07-18T19:20:32.530Z
Learning: Applies to dashboard/**/*client.tsx : Interactive UI that relies on hooks (`useState`, `useEffect`, React Query, wallet hooks).
Applied to files:
apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/support/_components/SupportCaseDetails.tsx
📚 Learning: 2025-07-18T19:20:32.530Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: .cursor/rules/dashboard.mdc:0-0
Timestamp: 2025-07-18T19:20:32.530Z
Learning: Applies to dashboard/**/*.{tsx,jsx} : Use `NavLink` (`@/components/ui/NavLink`) for internal navigation so active states are handled automatically.
Applied to files:
apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/support/_components/SupportCaseDetails.tsx
🧬 Code graph analysis (1)
apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/support/_components/SupportCaseDetails.tsx (6)
apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/support/types/tickets.ts (2)
SupportTicket(21-30)SupportMessage(1-11)apps/dashboard/src/@/analytics/report.ts (3)
reportSupportFeedbackStatusCheckFailed(602-608)reportSupportFeedbackSubmitted(564-571)reportSupportFeedbackFailed(582-591)apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/support/apis/feedback.ts (2)
checkFeedbackStatus(87-177)submitSupportFeedback(6-85)apps/portal/src/components/ui/textarea.tsx (1)
AutoResizeTextarea(26-48)apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/support/utils/ticket-status.ts (2)
getTicketStatusBadgeVariant(4-25)getTicketStatusLabel(27-46)apps/dashboard/src/app/(app)/components/ThirdwebMiniLogo.tsx (1)
ThirdwebMiniLogo(7-90)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (8)
- GitHub Check: Lint Packages
- GitHub Check: Size
- GitHub Check: Unit Tests
- GitHub Check: E2E Tests (pnpm, webpack)
- GitHub Check: E2E Tests (pnpm, vite)
- GitHub Check: Build Packages
- GitHub Check: E2E Tests (pnpm, esbuild)
- GitHub Check: Analyze (javascript)
🔇 Additional comments (3)
apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/support/_components/SupportCaseDetails.tsx (3)
116-185: LGTM! Well-structured feedback submission with comprehensive error handlingThe mutation includes proper validation, detailed error classification, analytics reporting, and user-friendly error messages. The optimistic cache update and toast notifications provide good UX.
587-595: LGTM! Clean wrapper implementation with error boundaryThe wrapper properly preserves the original component API while adding error boundary protection. This maintains backward compatibility while improving error resilience.
313-381: UI provides clear feedback for degraded status check behaviorThe conditional rendering correctly shows the warning banner when
statusCheckFailedis true, and the star rating system with accessible controls is well-implemented. The feedback submission UI is clean and functional.
...dashboard/src/app/(app)/team/[team_slug]/(team)/~/support/_components/SupportCaseDetails.tsx
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
♻️ Duplicate comments (3)
apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/support/_components/SupportCaseDetails.tsx (3)
276-297: Make star rating accessible and switch to design tokens (no raw colors)Add radiogroup semantics and replace hardcoded pink with tokens.
- <div className="flex gap-2 mb-6 mt-4"> + <div + className="flex gap-2 mb-6 mt-4" + role="radiogroup" + aria-label="Rate support experience" + > {[1, 2, 3, 4, 5].map((starValue) => ( <button key={`star-${starValue}`} type="button" onClick={() => handleStarClick(starValue - 1)} - className="transition-colors" + className="transition-colors" + role="radio" + aria-checked={starValue <= rating} aria-label={`Rate ${starValue} out of 5 stars`} > <StarIcon size={32} className={cn( "transition-colors", starValue <= rating - ? "text-pink-500 fill-current stroke-current" + ? "text-primary fill-current stroke-current" : "text-muted-foreground fill-current stroke-current", - "hover:text-pink-500", + "hover:text-primary", )} strokeWidth={starValue <= rating ? 2 : 1} /> </button> ))} </div>
288-293: Nit: replace raw color with token everywhereThere are two occurrences in this block; both updated in the larger diff above. Ensure no stray
text-pink-500remains.
259-263: Prevent duplicate submissions when status check fails (UI gate currently re-opens form)The condition
(!feedbackSubmitted || hasError)shows the form even after a successful submission if a later poll fails, enabling duplicates. Gate strictly on!feedbackSubmittedand surface the degraded banner separately.- {ticket.status === "closed" && - !isLoading && - (!feedbackSubmitted || hasError) && ( + {ticket.status === "closed" && + !isLoading && + !feedbackSubmitted && (Optionally show the warning banner outside this gate when
hasErroris true. Do you want a patch for that placement?
🧹 Nitpick comments (6)
apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/support/_components/SupportCaseDetails.tsx (6)
32-37: Types and API surface: prefer type alias, expose className, and add explicit return typeAligns with repo guidelines (type aliases, expose className on dashboard components, explicit return types).
-interface SupportCaseDetailsProps { - ticket: SupportTicket; - team: Team; -} - -function SupportCaseDetails({ ticket, team }: SupportCaseDetailsProps) { +type SupportCaseDetailsProps = { + ticket: SupportTicket; + team: Team; + className?: string; +}; + +function SupportCaseDetails({ ticket, team, className }: SupportCaseDetailsProps): JSX.Element {
209-210: Plumb className through the root elementLet parents style/layout this component.
- <div className="flex flex-col grow"> + <div className={cn("flex flex-col grow", className)}>
368-373: Add explicit return types to helper componentsSmall clarity/consistency improvement.
-function TicketHeader(props: { +function TicketHeader(props: { title: string; createdAt: string; updatedAt: string; status: SupportTicket["status"]; -}) { +}): JSX.Element {-function TicketMessage(props: { message: SupportMessage }) { +function TicketMessage(props: { message: SupportMessage }): JSX.Element {Also applies to: 404-405
214-216: Use NavLink for internal nav per dashboard guidelineBreadcrumb links are internal; prefer NavLink for active state consistency.
-import Link from "next/link"; +import Link from "next/link"; // if NavLink unavailable in breadcrumb, ignore +// import { NavLink } from "@/components/ui/nav-link"; @@ - <Link href={`/team/${team.slug}/~/support`}>Cases</Link> + {/* If BreadcrumbLink supports asChild with NavLink in your UI kit: */} + {/* <NavLink href={`/team/${team.slug}/~/support`}>Cases</NavLink> */} + <Link href={`/team/${team.slug}/~/support`}>Cases</Link>If the project exposes a breadcrumb-aware NavLink, switch to it; otherwise, this is non-blocking.
184-191: Optional: keep the error for send-reply in logs/analyticsBare
catch {}hides details; consider logging/reporting while keeping the user-facing toast.- } catch { - toast.error("Failed to send Message. Please try again."); + } catch (err) { + // Consider reporting to analytics; keep UI non-noisy + console.error("[SUPPORT] Reply send failed", err); + toast.error("Failed to send message. Please try again.");
73-74: Minor: consider retry: 0 for faster UXGiven the non-blocking strategy, a single retry may still delay UI;
retry: 0is also reasonable.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (1)
apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/support/_components/SupportCaseDetails.tsx(6 hunks)
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.{ts,tsx}: Write idiomatic TypeScript with explicit function declarations and return types
Limit each file to one stateless, single-responsibility function for clarity
Re-use shared types from@/typesor localtypes.tsbarrels
Prefer type aliases over interface except for nominal shapes
Avoidanyandunknownunless unavoidable; narrow generics when possible
Choose composition over inheritance; leverage utility types (Partial,Pick, etc.)
Comment only ambiguous logic; avoid restating TypeScript in prose
**/*.{ts,tsx}: Use explicit function declarations and explicit return types in TypeScript
Limit each file to one stateless, single‑responsibility function
Re‑use shared types from@/typeswhere applicable
Prefertypealiases overinterfaceexcept for nominal shapes
Avoidanyandunknownunless unavoidable; narrow generics when possible
Prefer composition over inheritance; use utility types (Partial, Pick, etc.)
Lazy‑import optional features and avoid top‑level side‑effects to reduce bundle size
Files:
apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/support/_components/SupportCaseDetails.tsx
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (CLAUDE.md)
Load heavy dependencies inside async paths to keep initial bundle lean (lazy loading)
Files:
apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/support/_components/SupportCaseDetails.tsx
apps/{dashboard,playground-web}/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
apps/{dashboard,playground-web}/**/*.{ts,tsx}: Import UI primitives from@/components/ui/*(Button, Input, Select, Tabs, Card, Sidebar, Badge, Separator) in dashboard and playground apps
UseNavLinkfor internal navigation with automatic active states in dashboard and playground apps
Use Tailwind CSS only – no inline styles or CSS modules
Usecn()from@/lib/utilsfor conditional class logic
Use design system tokens (e.g.,bg-card,border-border,text-muted-foreground)
Server Components (Node edge): Start files withimport "server-only";
Client Components (browser): Begin files with'use client';
Always callgetAuthToken()to retrieve JWT from cookies on server side
UseAuthorization: Bearerheader – never embed tokens in URLs
Return typed results (e.g.,Project[],User[]) – avoidany
Wrap client-side data fetching calls in React Query (@tanstack/react-query)
Use descriptive, stablequeryKeysfor React Query cache hits
ConfigurestaleTime/cacheTimein React Query based on freshness (default ≥ 60s)
Keep tokens secret via internal API routes or server actions
Never importposthog-jsin server components
Files:
apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/support/_components/SupportCaseDetails.tsx
apps/{dashboard,playground}/**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
apps/{dashboard,playground}/**/*.{ts,tsx}: Import UI primitives from@/components/ui/_(e.g., Button, Input, Tabs, Card)
UseNavLinkfor internal navigation to get active state handling
Use Tailwind CSS for styling; no inline styles
Merge class names withcn()from@/lib/utilsfor conditional classes
Stick to design tokens (e.g., bg-card, border-border, text-muted-foreground)
Server Components must start withimport "server-only"; usenext/headers, server‑only env, heavy data fetching, andredirect()where appropriate
Client Components must start with'use client'; handle interactivity with hooks and browser APIs
Server-side data fetching: callgetAuthToken()from cookies, sendAuthorization: Bearer <token>header, and return typed results (avoidany)
Client-side data fetching: wrap calls in React Query with descriptive, stablequeryKeysand set sensiblestaleTime/cacheTime(≥ 60s default); keep tokens secret via internal routes or server actions
Do not importposthog-jsin server components (client-side only)
Files:
apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/support/_components/SupportCaseDetails.tsx
apps/{dashboard,playground}/**/*.tsx
📄 CodeRabbit inference engine (AGENTS.md)
Expose a
classNameprop on the root element of every component
Files:
apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/support/_components/SupportCaseDetails.tsx
🧠 Learnings (9)
📚 Learning: 2025-05-26T16:31:02.480Z
Learnt from: MananTank
PR: thirdweb-dev/js#7152
File: apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/claim-conditions/shared-claim-conditions-page.tsx:43-49
Timestamp: 2025-05-26T16:31:02.480Z
Learning: In the thirdweb dashboard codebase, when `redirectToContractLandingPage()` is called, an explicit return statement is not required afterward because the function internally calls Next.js's `redirect()` which throws an error to halt execution.
Applied to files:
apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/support/_components/SupportCaseDetails.tsx
📚 Learning: 2025-08-29T15:37:38.513Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: AGENTS.md:0-0
Timestamp: 2025-08-29T15:37:38.513Z
Learning: Applies to apps/{dashboard,playground}/**/*.{ts,tsx} : Client-side data fetching: wrap calls in React Query with descriptive, stable `queryKeys` and set sensible `staleTime/cacheTime` (≥ 60s default); keep tokens secret via internal routes or server actions
Applied to files:
apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/support/_components/SupportCaseDetails.tsx
📚 Learning: 2025-06-10T00:50:20.795Z
Learnt from: MananTank
PR: thirdweb-dev/js#7315
File: apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/assets/create/nft/launch-nft.tsx:153-226
Timestamp: 2025-06-10T00:50:20.795Z
Learning: In apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/assets/create/nft/launch-nft.tsx, the updateStatus function correctly expects a complete MultiStepState["status"] object. For pending states, { type: "pending" } is the entire status object. For error states, { type: "error", message: React.ReactNode } is the entire status object. The current code incorrectly spreads the entire step object instead of passing just the status object.
Applied to files:
apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/support/_components/SupportCaseDetails.tsx
📚 Learning: 2025-07-18T19:19:55.613Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: CLAUDE.md:0-0
Timestamp: 2025-07-18T19:19:55.613Z
Learning: Applies to src/@/analytics/report.ts : Review `src/@/analytics/report.ts` before adding analytics events to check for duplicates
Applied to files:
apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/support/_components/SupportCaseDetails.tsx
📚 Learning: 2025-08-29T15:37:38.513Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: AGENTS.md:0-0
Timestamp: 2025-08-29T15:37:38.513Z
Learning: Applies to apps/{dashboard,playground}/src/@/analytics/report.ts : Check `report.ts` before adding a new analytics event to avoid duplicates
Applied to files:
apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/support/_components/SupportCaseDetails.tsx
📚 Learning: 2025-07-18T19:20:32.530Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: .cursor/rules/dashboard.mdc:0-0
Timestamp: 2025-07-18T19:20:32.530Z
Learning: Applies to dashboard/**/*.{tsx,jsx} : Stick to design-tokens: background (`bg-card`), borders (`border-border`), muted text (`text-muted-foreground`) etc.
Applied to files:
apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/support/_components/SupportCaseDetails.tsx
📚 Learning: 2025-08-29T15:37:38.513Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: AGENTS.md:0-0
Timestamp: 2025-08-29T15:37:38.513Z
Learning: Applies to apps/{dashboard,playground}/**/*.{ts,tsx} : Stick to design tokens (e.g., bg-card, border-border, text-muted-foreground)
Applied to files:
apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/support/_components/SupportCaseDetails.tsx
📚 Learning: 2025-07-18T19:19:55.613Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: CLAUDE.md:0-0
Timestamp: 2025-07-18T19:19:55.613Z
Learning: Applies to apps/{dashboard,playground-web}/**/*.{ts,tsx} : Use design system tokens (e.g., `bg-card`, `border-border`, `text-muted-foreground`)
Applied to files:
apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/support/_components/SupportCaseDetails.tsx
📚 Learning: 2025-07-18T19:20:32.530Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: .cursor/rules/dashboard.mdc:0-0
Timestamp: 2025-07-18T19:20:32.530Z
Learning: Applies to dashboard/**/*.{tsx,jsx} : Prefer composable primitives over custom markup: `Button`, `Input`, `Select`, `Tabs`, `Card`, `Sidebar`, `Separator`, `Badge`.
Applied to files:
apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/support/_components/SupportCaseDetails.tsx
🧬 Code graph analysis (1)
apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/support/_components/SupportCaseDetails.tsx (2)
apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/support/apis/feedback.ts (2)
checkFeedbackStatus(87-177)submitSupportFeedback(6-85)apps/portal/src/components/ui/textarea.tsx (1)
AutoResizeTextarea(26-48)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
- GitHub Check: E2E Tests (pnpm, esbuild)
- GitHub Check: Size
- GitHub Check: Unit Tests
- GitHub Check: Build Packages
- GitHub Check: Analyze (javascript)
🔇 Additional comments (2)
apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/support/_components/SupportCaseDetails.tsx (2)
45-53: LGTM: simplified non-throwing status error handlerThe local degraded state avoids React Query error flow and matches the non-blocking UX.
24-25: Server Actions used—no secret exposure
Thefeedback.tsmodule is marked with"use server", socheckFeedbackStatusandsubmitSupportFeedbackexecute exclusively on the server via Next.js Server Actions and will not bundleSERVICE_AUTH_KEY_SIWAorx-service-api-keyin the browser. (nextjs.org)Likely an incorrect or invalid review comment.
Merge activity
|
<!--
## title your PR with this format: "[SDK/Dashboard/Portal] Feature/Fix: Concise title for the changes"
If you did not copy the branch name from Linear, paste the issue tag here (format is TEAM-0000):
## Notes for the reviewer
Anything important to call out? Be sure to also clarify these in your comments.
## How to test
Unit tests, playground, etc.
-->
<!-- start pr-codex -->
---
## PR-Codex overview
This PR focuses on enhancing input validation and error handling in the `submitSupportFeedback` and `checkFeedbackStatus` functions, as well as improving the `SupportCaseDetails` component for better user experience during feedback submission.
### Detailed summary
- Improved input validation in `submitSupportFeedback` for `ticketId` and `rating`.
- Enhanced error handling with more specific error messages.
- Added comprehensive validation for API response structure.
- Updated `SupportCaseDetails` to handle status check errors non-blockingly.
- Adjusted feedback submission logic to improve user experience and error reporting.
- Refactored button and feedback display logic for clarity and usability.
> ✨ Ask PR-Codex anything about this PR by commenting with `/codex {your question}`
<!-- end pr-codex -->
<!-- This is an auto-generated comment: release notes by coderabbit.ai -->
## Summary by CodeRabbit
* **New Features**
* Interactive 5‑star rating, accessible controls, auto‑resizing feedback textarea, and integrated Send Feedback button with spinner.
* “Checking feedback status…” loader and clear “Thank you” state after successful submission.
* **Improvements**
* Non‑blocking status check with a user‑facing warning if verification fails.
* Faster, clearer error messages and timeouts for network/server issues; form validation prevents invalid submissions.
* Feedback resets and success toasts on submission.
* **Bug Fixes**
* Feedback UI no longer fails when status verification errors occur; malformed API responses handled gracefully.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
1c597dc to
bd17c08
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🧹 Nitpick comments (5)
apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/support/apis/feedback.ts (3)
16-24: Normalize and reuse trimmed ticketIdTrim once and use the canonical value in the payload to avoid storing/processing trailing spaces.
- if (!ticketId?.trim()) { + const tid = ticketId?.trim(); + if (!tid) { return { error: "ticketId is required." }; } @@ - const payload = { + const payload = { rating: rating.toString(), feedback: normalizedFeedback, - ticket_id: ticketId, + ticket_id: tid, };Also applies to: 44-48
65-68: Cap server error text to prevent oversized messagesSome backends return large HTML error pages. Consider truncating for UX and logs (keep status intact).
- const errorText = await response.text(); + const raw = await response.text(); + const errorText = raw.length > 500 ? `${raw.slice(0, 500)}…` : raw;
122-136: Avoid sending Content-Type on GET; prefer Accept headerSetting Content-Type on GET is unnecessary and can confuse some intermediaries. Ask for JSON via Accept instead.
{ method: "GET", cache: "no-store", - headers: { - "Content-Type": "application/json", - "x-service-api-key": apiKey, - }, + headers: { + Accept: "application/json", + "x-service-api-key": apiKey, + }, signal: ac.signal, },apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/support/_components/SupportCaseDetails.tsx (2)
119-127: Handle timeout errors explicitly in user messageAPI returns “Request timeout”; surface a clearer toast for that case too.
- } else if (/API Server error/i.test(msg)) { + } else if (/API Server error/i.test(msg)) { message = "Server error. Please try again later."; + } else if (/timeout/i.test(msg)) { + message = "Request timed out. Please try again."; }
276-297: Make star rating accessible and use design tokens over raw colorExpose radiogroup semantics and replace
text-pink-500withtext-primaryper design tokens.- <div className="flex gap-2 mb-6 mt-4"> + <div + className="flex gap-2 mb-6 mt-4" + role="radiogroup" + aria-label="Rate support experience" + onKeyDown={handleRatingKeyDown} + > {[1, 2, 3, 4, 5].map((starValue) => ( <button key={`star-${starValue}`} type="button" onClick={() => handleStarClick(starValue - 1)} - className="transition-colors" + className="transition-colors" + role="radio" + aria-checked={starValue === rating} aria-label={`Rate ${starValue} out of 5 stars`} > <StarIcon size={32} className={cn( "transition-colors", starValue <= rating - ? "text-pink-500 fill-current stroke-current" + ? "text-primary fill-current stroke-current" : "text-muted-foreground fill-current stroke-current", - "hover:text-pink-500", + "hover:text-primary", )} strokeWidth={starValue <= rating ? 2 : 1} /> </button> ))} </div>Add this helper near other handlers:
function handleRatingKeyDown(e: React.KeyboardEvent) { if (e.key === "ArrowRight" || e.key === "ArrowUp") { e.preventDefault(); setRating((r) => Math.min(5, Math.max(1, (r || 0) + 1))); } else if (e.key === "ArrowLeft" || e.key === "ArrowDown") { e.preventDefault(); setRating((r) => Math.min(5, Math.max(1, (r || 0) - 1))); } else if (e.key === "Home") { e.preventDefault(); setRating(1); } else if (e.key === "End") { e.preventDefault(); setRating(5); } }Also applies to: 287-293
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (2)
apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/support/_components/SupportCaseDetails.tsx(6 hunks)apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/support/apis/feedback.ts(4 hunks)
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.{ts,tsx}: Write idiomatic TypeScript with explicit function declarations and return types
Limit each file to one stateless, single-responsibility function for clarity
Re-use shared types from@/typesor localtypes.tsbarrels
Prefer type aliases over interface except for nominal shapes
Avoidanyandunknownunless unavoidable; narrow generics when possible
Choose composition over inheritance; leverage utility types (Partial,Pick, etc.)
Comment only ambiguous logic; avoid restating TypeScript in prose
**/*.{ts,tsx}: Use explicit function declarations and explicit return types in TypeScript
Limit each file to one stateless, single‑responsibility function
Re‑use shared types from@/typeswhere applicable
Prefertypealiases overinterfaceexcept for nominal shapes
Avoidanyandunknownunless unavoidable; narrow generics when possible
Prefer composition over inheritance; use utility types (Partial, Pick, etc.)
Lazy‑import optional features and avoid top‑level side‑effects to reduce bundle size
Files:
apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/support/apis/feedback.tsapps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/support/_components/SupportCaseDetails.tsx
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (CLAUDE.md)
Load heavy dependencies inside async paths to keep initial bundle lean (lazy loading)
Files:
apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/support/apis/feedback.tsapps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/support/_components/SupportCaseDetails.tsx
apps/{dashboard,playground-web}/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
apps/{dashboard,playground-web}/**/*.{ts,tsx}: Import UI primitives from@/components/ui/*(Button, Input, Select, Tabs, Card, Sidebar, Badge, Separator) in dashboard and playground apps
UseNavLinkfor internal navigation with automatic active states in dashboard and playground apps
Use Tailwind CSS only – no inline styles or CSS modules
Usecn()from@/lib/utilsfor conditional class logic
Use design system tokens (e.g.,bg-card,border-border,text-muted-foreground)
Server Components (Node edge): Start files withimport "server-only";
Client Components (browser): Begin files with'use client';
Always callgetAuthToken()to retrieve JWT from cookies on server side
UseAuthorization: Bearerheader – never embed tokens in URLs
Return typed results (e.g.,Project[],User[]) – avoidany
Wrap client-side data fetching calls in React Query (@tanstack/react-query)
Use descriptive, stablequeryKeysfor React Query cache hits
ConfigurestaleTime/cacheTimein React Query based on freshness (default ≥ 60s)
Keep tokens secret via internal API routes or server actions
Never importposthog-jsin server components
Files:
apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/support/apis/feedback.tsapps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/support/_components/SupportCaseDetails.tsx
apps/{dashboard,playground}/**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
apps/{dashboard,playground}/**/*.{ts,tsx}: Import UI primitives from@/components/ui/_(e.g., Button, Input, Tabs, Card)
UseNavLinkfor internal navigation to get active state handling
Use Tailwind CSS for styling; no inline styles
Merge class names withcn()from@/lib/utilsfor conditional classes
Stick to design tokens (e.g., bg-card, border-border, text-muted-foreground)
Server Components must start withimport "server-only"; usenext/headers, server‑only env, heavy data fetching, andredirect()where appropriate
Client Components must start with'use client'; handle interactivity with hooks and browser APIs
Server-side data fetching: callgetAuthToken()from cookies, sendAuthorization: Bearer <token>header, and return typed results (avoidany)
Client-side data fetching: wrap calls in React Query with descriptive, stablequeryKeysand set sensiblestaleTime/cacheTime(≥ 60s default); keep tokens secret via internal routes or server actions
Do not importposthog-jsin server components (client-side only)
Files:
apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/support/apis/feedback.tsapps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/support/_components/SupportCaseDetails.tsx
apps/{dashboard,playground}/**/*.tsx
📄 CodeRabbit inference engine (AGENTS.md)
Expose a
classNameprop on the root element of every component
Files:
apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/support/_components/SupportCaseDetails.tsx
🧠 Learnings (9)
📚 Learning: 2025-05-26T16:31:02.480Z
Learnt from: MananTank
PR: thirdweb-dev/js#7152
File: apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/claim-conditions/shared-claim-conditions-page.tsx:43-49
Timestamp: 2025-05-26T16:31:02.480Z
Learning: In the thirdweb dashboard codebase, when `redirectToContractLandingPage()` is called, an explicit return statement is not required afterward because the function internally calls Next.js's `redirect()` which throws an error to halt execution.
Applied to files:
apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/support/_components/SupportCaseDetails.tsx
📚 Learning: 2025-08-29T15:37:38.513Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: AGENTS.md:0-0
Timestamp: 2025-08-29T15:37:38.513Z
Learning: Applies to apps/{dashboard,playground}/**/*.{ts,tsx} : Client-side data fetching: wrap calls in React Query with descriptive, stable `queryKeys` and set sensible `staleTime/cacheTime` (≥ 60s default); keep tokens secret via internal routes or server actions
Applied to files:
apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/support/_components/SupportCaseDetails.tsx
📚 Learning: 2025-06-10T00:50:20.795Z
Learnt from: MananTank
PR: thirdweb-dev/js#7315
File: apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/assets/create/nft/launch-nft.tsx:153-226
Timestamp: 2025-06-10T00:50:20.795Z
Learning: In apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/assets/create/nft/launch-nft.tsx, the updateStatus function correctly expects a complete MultiStepState["status"] object. For pending states, { type: "pending" } is the entire status object. For error states, { type: "error", message: React.ReactNode } is the entire status object. The current code incorrectly spreads the entire step object instead of passing just the status object.
Applied to files:
apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/support/_components/SupportCaseDetails.tsx
📚 Learning: 2025-07-18T19:19:55.613Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: CLAUDE.md:0-0
Timestamp: 2025-07-18T19:19:55.613Z
Learning: Applies to src/@/analytics/report.ts : Review `src/@/analytics/report.ts` before adding analytics events to check for duplicates
Applied to files:
apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/support/_components/SupportCaseDetails.tsx
📚 Learning: 2025-08-29T15:37:38.513Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: AGENTS.md:0-0
Timestamp: 2025-08-29T15:37:38.513Z
Learning: Applies to apps/{dashboard,playground}/src/@/analytics/report.ts : Check `report.ts` before adding a new analytics event to avoid duplicates
Applied to files:
apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/support/_components/SupportCaseDetails.tsx
📚 Learning: 2025-07-18T19:20:32.530Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: .cursor/rules/dashboard.mdc:0-0
Timestamp: 2025-07-18T19:20:32.530Z
Learning: Applies to dashboard/**/*.{tsx,jsx} : Stick to design-tokens: background (`bg-card`), borders (`border-border`), muted text (`text-muted-foreground`) etc.
Applied to files:
apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/support/_components/SupportCaseDetails.tsx
📚 Learning: 2025-08-29T15:37:38.513Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: AGENTS.md:0-0
Timestamp: 2025-08-29T15:37:38.513Z
Learning: Applies to apps/{dashboard,playground}/**/*.{ts,tsx} : Stick to design tokens (e.g., bg-card, border-border, text-muted-foreground)
Applied to files:
apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/support/_components/SupportCaseDetails.tsx
📚 Learning: 2025-07-18T19:19:55.613Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: CLAUDE.md:0-0
Timestamp: 2025-07-18T19:19:55.613Z
Learning: Applies to apps/{dashboard,playground-web}/**/*.{ts,tsx} : Use design system tokens (e.g., `bg-card`, `border-border`, `text-muted-foreground`)
Applied to files:
apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/support/_components/SupportCaseDetails.tsx
📚 Learning: 2025-07-18T19:20:32.530Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: .cursor/rules/dashboard.mdc:0-0
Timestamp: 2025-07-18T19:20:32.530Z
Learning: Applies to dashboard/**/*.{tsx,jsx} : Prefer composable primitives over custom markup: `Button`, `Input`, `Select`, `Tabs`, `Card`, `Sidebar`, `Separator`, `Badge`.
Applied to files:
apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/support/_components/SupportCaseDetails.tsx
🧬 Code graph analysis (1)
apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/support/_components/SupportCaseDetails.tsx (2)
apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/support/apis/feedback.ts (2)
checkFeedbackStatus(96-186)submitSupportFeedback(6-94)apps/portal/src/components/ui/textarea.tsx (1)
AutoResizeTextarea(26-48)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
- GitHub Check: Size
- GitHub Check: Lint Packages
- GitHub Check: Analyze (javascript)
🔇 Additional comments (6)
apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/support/apis/feedback.ts (2)
70-85: Response parsing and shape checks look solidGood JSON try/catch and minimal shape validation before trusting fields. LGTM.
87-93: Good differentiation of timeout vs. network vs. generic errorsThis maps well to fetch semantics. LGTM.
apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/support/_components/SupportCaseDetails.tsx (4)
45-53: Non-blocking degraded state handler looks goodSimple, predictable flag; prevents React Query error gating from hiding the warning. LGTM.
259-262: Confirm UX: form is shown when status check fails even if feedback exists
(!feedbackSubmitted || hasError)can allow duplicate submissions during transient API failures. If that’s intentional, ignore. If not, gate strictly on!feedbackSubmitted && !hasErroror block submit whenhasErroris true.Option A (strict):
- (!feedbackSubmitted || hasError) && ( + (!feedbackSubmitted && !hasError) && (Option B (block on submit):
- Keep UI visible, but in
handleSendFeedback, bail out whenhasErroris true and ask the user to retry after a moment.
98-105: Nice: immediate optimistic UI update after successful submitResetting state and marking cache true avoids a second fetch. LGTM.
Also applies to: 109-114
486-487: Export pattern is cleanLocal function + named export keeps tree-shaking friendly and aligns with the file’s client component directive. LGTM.
| let data: { | ||
| has_feedback: boolean; | ||
| feedback_data: { | ||
| id: string; | ||
| rating: number | null; | ||
| feedback: string | null; | ||
| ticket_id: string | null; | ||
| created_at: string; | ||
| } | null; | ||
| }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Fix: Missing object guard can throw TypeError and be misclassified as “Network error”
If the API returns null or a primitive, property access on data can throw and fall into the catch where TypeError is reported as network failure. Guard the JSON value before field access and avoid over-confident typing of data.
- let data: {
- has_feedback: boolean;
- feedback_data: {
- id: string;
- rating: number | null;
- feedback: string | null;
- ticket_id: string | null;
- created_at: string;
- } | null;
- };
+ let data: unknown;
@@
- data = await response.json();
+ data = await response.json();
} catch (_jsonError) {
return { error: "Invalid JSON response from API" };
}
- // Comprehensive validation of the API response structure
- if (
- typeof data.has_feedback !== "boolean" ||
- (data.feedback_data != null &&
- (typeof data.feedback_data !== "object" ||
- typeof data.feedback_data.id !== "string" ||
- (data.feedback_data.rating != null &&
- typeof data.feedback_data.rating !== "number") ||
- (data.feedback_data.feedback != null &&
- typeof data.feedback_data.feedback !== "string") ||
- (data.feedback_data.ticket_id != null &&
- typeof data.feedback_data.ticket_id !== "string") ||
- typeof data.feedback_data.created_at !== "string"))
- ) {
- return { error: "Invalid response format from API" };
- }
-
- return { hasFeedback: data.has_feedback };
+ // Guard before accessing properties
+ if (typeof data !== "object" || data === null) {
+ return { error: "Invalid response format from API" };
+ }
+ const d = data as {
+ has_feedback?: unknown;
+ feedback_data?: {
+ id?: unknown;
+ rating?: unknown;
+ feedback?: unknown;
+ ticket_id?: unknown;
+ created_at?: unknown;
+ } | null;
+ };
+
+ if (
+ typeof d.has_feedback !== "boolean" ||
+ (d.feedback_data != null &&
+ (typeof d.feedback_data !== "object" ||
+ typeof d.feedback_data.id !== "string" ||
+ (d.feedback_data.rating != null &&
+ typeof d.feedback_data.rating !== "number") ||
+ (d.feedback_data.feedback != null &&
+ typeof d.feedback_data.feedback !== "string") ||
+ (d.feedback_data.ticket_id != null &&
+ typeof d.feedback_data.ticket_id !== "string") ||
+ typeof d.feedback_data.created_at !== "string"))
+ ) {
+ return { error: "Invalid response format from API" };
+ }
+
+ return { hasFeedback: d.has_feedback };Follow-up: With this guard in place, genuine runtime shape errors won’t be misreported as network faults by the TypeError branch below.
Also applies to: 154-159, 160-175, 177-177, 179-185
PR-Codex overview
This PR focuses on enhancing input validation and error handling in the
submitSupportFeedbackandcheckFeedbackStatusfunctions, as well as improving user experience in theSupportCaseDetailscomponent with better feedback mechanisms.Detailed summary
ticketIdandratinginsubmitSupportFeedback.submitSupportFeedbackandcheckFeedbackStatus.SupportCaseDetailsfor status check failures.Summary by CodeRabbit
New Features
Improvements
Bug Fixes