Skip to content

Commit 3ed3dd1

Browse files
committed
add supabase table and set up
1 parent 94edc38 commit 3ed3dd1

File tree

8 files changed

+419
-49
lines changed

8 files changed

+419
-49
lines changed

.vscode/settings.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
"editor.defaultFormatter": "biomejs.biome"
1616
},
1717
"[typescriptreact]": {
18-
"editor.defaultFormatter": "biomejs.biome"
18+
"editor.defaultFormatter": "rvest.vs-code-prettier-eslint"
1919
},
2020
"editor.codeActionsOnSave": {
2121
"quickfix.biome": "explicit",

apps/dashboard/FEEDBACK_FEATURE.md

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
# Support Feedback Feature
2+
3+
## Overview
4+
5+
Added a feedback collection system to support tickets that allows users to rate their experience and provide optional comments when tickets are closed.
6+
7+
## Features
8+
9+
- **Star Rating System**: 1-5 star rating with visual feedback
10+
- **Text Feedback**: Optional text input for detailed comments
11+
- **Automatic Display**: Feedback form appears when support tickets are closed
12+
- **Data Storage**: Feedback is stored in Supabase database
13+
- **Success Feedback**: Toast notifications and form reset after submission
14+
15+
## Implementation
16+
17+
### Database
18+
19+
- **Table**: `support_feedback`
20+
- **Columns**:
21+
- `id` (UUID, Primary Key)
22+
- `created_at` (Timestamp, auto-generated)
23+
- `rating` (Integer, 1-5)
24+
- `feedback` (Text, optional)
25+
26+
### Files Added/Modified
27+
28+
- `src/@/lib/supabase.ts` - Supabase client configuration
29+
- `src/app/(app)/team/[team_slug]/(team)/~/support/apis/feedback.ts` - Feedback submission API
30+
- `src/app/(app)/team/[team_slug]/(team)/~/support/_components/SupportCaseDetails.tsx` - Updated with feedback form
31+
32+
### Dependencies
33+
34+
- `@supabase/supabase-js` - Added for database operations
35+
36+
## Environment Variables Required
37+
38+
```bash
39+
NEXT_PUBLIC_SUPABASE_URL=your_supabase_project_url
40+
NEXT_PUBLIC_SUPABASE_ANON_KEY=your_supabase_anon_key
41+
```
42+
43+
## Usage
44+
45+
1. Navigate to a closed support ticket
46+
2. The feedback form will appear at the bottom
47+
3. Rate the experience (1-5 stars)
48+
4. Add optional text feedback
49+
5. Click "Send Feedback"
50+
6. Data is saved to Supabase and form resets
51+
52+
## Database Setup
53+
54+
Run this SQL in your Supabase SQL Editor:
55+
56+
```sql
57+
CREATE TABLE support_feedback (
58+
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
59+
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
60+
rating INTEGER NOT NULL,
61+
feedback TEXT
62+
);
63+
```

apps/dashboard/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
"@scalar/api-reference-react": "0.7.25",
2323
"@sentry/nextjs": "9.34.0",
2424
"@shazow/whatsabi": "0.22.2",
25+
"@supabase/supabase-js": "^2.55.0",
2526
"@tanstack/react-query": "5.81.5",
2627
"@tanstack/react-table": "^8.21.3",
2728
"@thirdweb-dev/service-utils": "workspace:*",

apps/dashboard/public/robots.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# *
22
User-agent: *
3-
Allow: /
3+
Disallow: /
44

55
# Host
66
Host: https://thirdweb.com
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { createClient } from "@supabase/supabase-js";
2+
3+
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL;
4+
const supabaseAnonKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY;
5+
6+
// Only create client if environment variables are available
7+
export const supabase =
8+
supabaseUrl && supabaseAnonKey
9+
? createClient(supabaseUrl, supabaseAnonKey)
10+
: null;
11+
12+
// Log the status for debugging
13+
if (process.env.NODE_ENV === "development") {
14+
console.log("🔧 Supabase client status:", {
15+
hasUrl: !!supabaseUrl,
16+
hasKey: !!supabaseAnonKey,
17+
clientCreated: !!supabase,
18+
});
19+
}

apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/support/_components/SupportCaseDetails.tsx

Lines changed: 57 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import { Spinner } from "@/components/ui/Spinner/Spinner";
2020
import { AutoResizeTextarea } from "@/components/ui/textarea";
2121
import { cn } from "@/lib/utils";
2222
import { ThirdwebMiniLogo } from "../../../../../../components/ThirdwebMiniLogo";
23+
import { submitSupportFeedback } from "../apis/feedback";
2324
import { sendMessageToTicket } from "../apis/support";
2425
import type { SupportMessage, SupportTicket } from "../types/tickets";
2526
import {
@@ -38,17 +39,37 @@ export function SupportCaseDetails({ ticket, team }: SupportCaseDetailsProps) {
3839
const [localMessages, setLocalMessages] = useState(ticket.messages || []);
3940

4041
// rating/feedback
41-
const [rating, setRating] = useState(0)
42-
const [feedback, setFeedback] = useState("")
42+
const [rating, setRating] = useState(0);
43+
const [feedback, setFeedback] = useState("");
4344

4445
const handleStarClick = (starIndex: number) => {
45-
setRating(starIndex + 1)
46-
}
46+
setRating(starIndex + 1);
47+
};
48+
49+
const handleSendFeedback = async () => {
50+
if (rating === 0) {
51+
toast.error("Please select a rating");
52+
return;
53+
}
54+
55+
try {
56+
const result = await submitSupportFeedback({
57+
rating,
58+
feedback,
59+
});
4760

48-
const handleSendFeedback = () => {
49-
console.log("Feedback sent:", { rating, feedback })
50-
// Handle feedback submission
51-
}
61+
if ("error" in result) {
62+
throw new Error(result.error);
63+
}
64+
65+
toast.success("Thank you for your feedback!");
66+
setRating(0);
67+
setFeedback("");
68+
} catch (error) {
69+
console.error("Failed to submit feedback:", error);
70+
toast.error("Failed to submit feedback. Please try again.");
71+
}
72+
};
5273

5374
const handleSendReply = async () => {
5475
if (!team.unthreadCustomerId) {
@@ -165,31 +186,38 @@ export function SupportCaseDetails({ ticket, team }: SupportCaseDetailsProps) {
165186
{ticket.status === "closed" && (
166187
<div className="border-t p-6">
167188
<p className="text-muted-foreground text-sm">
168-
This ticket is closed. Give us a quick rating to let us know how we did!
189+
This ticket is closed. Give us a quick rating to let us know how
190+
we did!
169191
</p>
170192
</div>
171193
)}
172194

173-
<div className="flex gap-2 mb-6 px-6">
174-
{[...Array(5)].map((_, index) => (
175-
<button key={index} onClick={() => handleStarClick(index)} className="transition-colors">
176-
<svg
177-
width="32"
178-
height="32"
179-
viewBox="0 0 24 24"
180-
fill={index < rating ? "#ff00aa" : "none"}
181-
stroke={index < rating ? "#ff00aa" : "#666"}
182-
strokeWidth={index < rating ? "2" : "1"}
183-
className="hover:fill-pink-500 hover:stroke-pink-500 rounded-sm"
184-
rx="2"
185-
>
186-
<polygon points="12,2 15.09,8.26 22,9.27 17,14.14 18.18,21.02 12,17.77 5.82,21.02 7,14.14 2,9.27 8.91,8.26" />
187-
</svg>
188-
</button>
189-
))}
190-
</div>
195+
<div className="flex gap-2 mb-6 px-6">
196+
{[1, 2, 3, 4, 5].map((starValue) => (
197+
<button
198+
key={`star-${starValue}`}
199+
type="button"
200+
onClick={() => handleStarClick(starValue - 1)}
201+
className="transition-colors"
202+
aria-label={`Rate ${starValue} out of 5 stars`}
203+
>
204+
<svg
205+
width="32"
206+
height="32"
207+
viewBox="0 0 24 24"
208+
fill={starValue <= rating ? "#ff00aa" : "none"}
209+
stroke={starValue <= rating ? "#ff00aa" : "#666"}
210+
strokeWidth={starValue <= rating ? "2" : "1"}
211+
className="hover:fill-pink-500 hover:stroke-pink-500 rounded-sm"
212+
rx="2"
213+
aria-hidden="true"
214+
>
215+
<polygon points="12,2 15.09,8.26 22,9.27 17,14.14 18.18,21.02 12,17.77 5.82,21.02 7,14.14 2,9.27 8.91,8.26" />
216+
</svg>
217+
</button>
218+
))}
219+
</div>
191220

192-
{/* Feedback Input */}
193221
<div className="relative p-6">
194222
<div className="relative">
195223
<textarea
@@ -199,6 +227,7 @@ export function SupportCaseDetails({ ticket, team }: SupportCaseDetailsProps) {
199227
className="text-muted-foreground text-sm w-full bg-black text-white rounded-lg p-4 pr-28 min-h-[100px] resize-none border border-[#262626] focus:border-[#262626] focus:outline-none placeholder-[#A1A1A1]"
200228
/>
201229
<button
230+
type="button"
202231
onClick={handleSendFeedback}
203232
className="absolute mb-2 bottom-3 right-3 bg-white text-black px-4 py-2 rounded-full text-sm font-medium hover:bg-gray-100 transition-colors"
204233
>
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
"use server";
2+
3+
import { supabase } from "@/lib/supabase";
4+
5+
interface FeedbackData {
6+
rating: number;
7+
feedback: string;
8+
}
9+
10+
export async function submitSupportFeedback(
11+
data: FeedbackData,
12+
): Promise<{ success: true } | { error: string }> {
13+
try {
14+
console.log("🔍 Debug - Feedback submission attempt:", {
15+
hasSupabase: !!supabase,
16+
data: data,
17+
});
18+
19+
if (!supabase) {
20+
throw new Error(
21+
"Supabase client not initialized. Please check your environment variables.",
22+
);
23+
}
24+
25+
// Test the connection first
26+
const { data: testData, error: testError } = await supabase
27+
.from("support_feedback")
28+
.select("id")
29+
.limit(1);
30+
31+
console.log("🔍 Debug - Supabase connection test:", {
32+
testData,
33+
testError,
34+
});
35+
36+
const { error } = await supabase.from("support_feedback").insert({
37+
rating: data.rating,
38+
feedback: data.feedback,
39+
});
40+
41+
if (error) {
42+
console.error("Supabase error:", error);
43+
return { error: `Failed to submit feedback: ${error.message}` };
44+
}
45+
46+
return { success: true };
47+
} catch (error) {
48+
console.error("Feedback submission error:", error);
49+
return {
50+
error: `Failed to submit feedback: ${error instanceof Error ? error.message : "Unknown error"}`,
51+
};
52+
}
53+
}

0 commit comments

Comments
 (0)