11import { NEXT_PUBLIC_THIRDWEB_API_HOST } from "@/constants/public-envs" ;
2- import type { SupportMessage , SupportTicket } from "../types/tickets" ;
2+ import type {
3+ SupportMessage ,
4+ SupportTicket ,
5+ SupportTicketListItem ,
6+ } from "../types/tickets" ;
37
48export async function getSupportTicketsByTeam ( params : {
59 teamSlug : string ;
610 authToken : string ;
7- } ) : Promise < SupportTicket [ ] > {
11+ } ) : Promise < SupportTicketListItem [ ] > {
812 const encodedTeamSlug = encodeURIComponent ( params . teamSlug ) ;
913 const apiUrl = `${ NEXT_PUBLIC_THIRDWEB_API_HOST } /v1/teams/${ encodedTeamSlug } /support-conversations/list` ;
1014
@@ -31,7 +35,7 @@ export async function getSupportTicketsByTeam(params: {
3135 throw new Error ( `Failed to fetch support tickets: ${ errorText } ` ) ;
3236 }
3337
34- const data : { data ?: SupportTicket [ ] } = await response . json ( ) ;
38+ const data : { data ?: SupportTicketListItem [ ] } = await response . json ( ) ;
3539 const conversations = data . data || [ ] ;
3640 return conversations ;
3741}
@@ -50,138 +54,124 @@ type RawSupportMessage = {
5054 // Add any other fields you use from the API
5155} ;
5256
53- export async function getSupportTicket (
54- ticketId : string ,
55- teamSlug : string ,
56- authToken : string ,
57- ) : Promise < { data : SupportTicket } | { error : string } | null > {
58- if ( ! ticketId || ! teamSlug ) {
59- return { error : "Ticket ID and team slug are required" } ;
60- }
61-
57+ export async function getSupportTicket ( params : {
58+ ticketId : string ;
59+ teamSlug : string ;
60+ authToken : string ;
61+ } ) : Promise < SupportTicket > {
6262 // URL encode the team slug to handle special characters like #
63- const encodedTeamSlug = encodeURIComponent ( teamSlug ) ;
64- const encodedTicketId = encodeURIComponent ( ticketId ) ;
63+ const encodedTeamSlug = encodeURIComponent ( params . teamSlug ) ;
64+ const encodedTicketId = encodeURIComponent ( params . ticketId ) ;
6565
6666 const messagesPayload = {
6767 limit : 100 ,
6868 descending : false ,
6969 } ;
7070
71- try {
72- // Fetch conversation details and messages in parallel
73- const [ conversationResponse , messagesResponse ] = await Promise . all ( [
74- fetch (
75- `${ NEXT_PUBLIC_THIRDWEB_API_HOST } /v1/teams/${ encodedTeamSlug } /support-conversations/${ encodedTicketId } ` ,
76- {
77- cache : "no-store" ,
78- headers : {
79- Accept : "application/json" ,
80- "Accept-Encoding" : "identity" ,
81- Authorization : `Bearer ${ authToken } ` ,
82- "Content-Type" : "application/json" ,
83- } ,
84- method : "GET" ,
71+ // Fetch conversation details and messages in parallel
72+ const [ conversationResponse , messagesResponse ] = await Promise . all ( [
73+ fetch (
74+ `${ NEXT_PUBLIC_THIRDWEB_API_HOST } /v1/teams/${ encodedTeamSlug } /support-conversations/${ encodedTicketId } ` ,
75+ {
76+ cache : "no-store" ,
77+ headers : {
78+ Accept : "application/json" ,
79+ "Accept-Encoding" : "identity" ,
80+ Authorization : `Bearer ${ params . authToken } ` ,
81+ "Content-Type" : "application/json" ,
8582 } ,
86- ) ,
87- fetch (
88- ` ${ NEXT_PUBLIC_THIRDWEB_API_HOST } /v1/teams/ ${ encodedTeamSlug } /support-conversations/ ${ encodedTicketId } /messages/list` ,
89- {
90- body : JSON . stringify ( messagesPayload ) ,
91- cache : "no-store" ,
92- headers : {
93- Accept : "application/json " ,
94- "Accept-Encoding" : "identity" ,
95- Authorization : `Bearer ${ authToken } ` ,
96- "Content-Type " : "application/json " ,
97- } ,
98- method : "POST " ,
83+ method : "GET" ,
84+ } ,
85+ ) ,
86+ fetch (
87+ ` ${ NEXT_PUBLIC_THIRDWEB_API_HOST } /v1/teams/ ${ encodedTeamSlug } /support-conversations/ ${ encodedTicketId } /messages/list` ,
88+ {
89+ body : JSON . stringify ( messagesPayload ) ,
90+ cache : "no-store " ,
91+ headers : {
92+ Accept : "application/json" ,
93+ "Accept-Encoding " : "identity " ,
94+ Authorization : `Bearer ${ params . authToken } ` ,
95+ "Content-Type" : "application/json " ,
9996 } ,
100- ) ,
101- ] ) ;
102-
103- if ( ! conversationResponse . ok ) {
104- if ( conversationResponse . status === 404 ) {
105- return null ; // Ticket not found
106- }
107- const errorText = await conversationResponse . text ( ) ;
108- return {
109- error : `API Server error: ${ conversationResponse . status } - ${ errorText } ` ,
110- } ;
111- }
112-
113- const conversation : SupportTicket = await conversationResponse . json ( ) ;
114-
115- // Fetch and map messages if the messages request was successful
116- if ( messagesResponse . ok ) {
117- const messagesData : { data ?: unknown [ ] } = await messagesResponse . json ( ) ;
118- const rawMessages = messagesData . data || [ ] ;
119- // Transform the raw messages to match our interface
120- const messages : SupportMessage [ ] = ( rawMessages as RawSupportMessage [ ] )
121- . filter ( ( msg ) => {
122- // Filter out messages without content - check both text and text fields
123- const hasContent = msg . text && msg . text . length > 0 ;
124- const hasText = msg . text && msg . text . trim ( ) . length > 0 ;
125- // Filter out private notes - they should not be shown to customers
126- const isNotPrivateNote = ! msg . isPrivateNote ;
127- return ( hasContent || hasText ) && isNotPrivateNote ;
128- } )
129- . map ( ( msg ) => {
130- // Use text if available and is a non-empty array, otherwise fall back to text
131- let content = "" ;
132- if ( typeof msg . text === "string" && msg . text . trim ( ) . length > 0 ) {
133- content = msg . text ;
134- }
135-
136- // Clean up 'Email:' line to show only the plain email if it contains a mailto link
137- if ( content ) {
138- content = content
139- . split ( "\n" )
140- . map ( ( line ) => {
141- if ( line . trim ( ) . toLowerCase ( ) . startsWith ( "email:" ) ) {
142- // Extract email from <mailto:...|...>
143- const match = line . match ( / < m a i l t o : ( [ ^ | > ] + ) \| [ ^ > ] + > / ) ;
144- if ( match ) {
145- return `Email: ${ match [ 1 ] } ` ;
146- }
97+ method : "POST" ,
98+ } ,
99+ ) ,
100+ ] ) ;
101+
102+ if ( ! conversationResponse . ok ) {
103+ throw new Error (
104+ `Failed to fetch support ticket: $${ await conversationResponse . text ( ) } ` ,
105+ ) ;
106+ }
107+
108+ const conversation : SupportTicket = await conversationResponse . json ( ) ;
109+
110+ // Fetch and map messages if the messages request was successful
111+ if ( messagesResponse . ok ) {
112+ const messagesData : { data ?: unknown [ ] } = await messagesResponse . json ( ) ;
113+ const rawMessages = messagesData . data || [ ] ;
114+ // Transform the raw messages to match our interface
115+ const messages : SupportMessage [ ] = ( rawMessages as RawSupportMessage [ ] )
116+ . filter ( ( msg ) => {
117+ // Filter out messages without content - check both text and text fields
118+ const hasContent = msg . text && msg . text . length > 0 ;
119+ const hasText = msg . text && msg . text . trim ( ) . length > 0 ;
120+ // Filter out private notes - they should not be shown to customers
121+ const isNotPrivateNote = ! msg . isPrivateNote ;
122+ return ( hasContent || hasText ) && isNotPrivateNote ;
123+ } )
124+ . map ( ( msg ) => {
125+ // Use text if available and is a non-empty array, otherwise fall back to text
126+ let content = "" ;
127+ if ( typeof msg . text === "string" && msg . text . trim ( ) . length > 0 ) {
128+ content = msg . text ;
129+ }
130+
131+ // Clean up 'Email:' line to show only the plain email if it contains a mailto link
132+ if ( content ) {
133+ content = content
134+ . split ( "\n" )
135+ . map ( ( line ) => {
136+ if ( line . trim ( ) . toLowerCase ( ) . startsWith ( "email:" ) ) {
137+ // Extract email from <mailto:...|...>
138+ const match = line . match ( / < m a i l t o : ( [ ^ | > ] + ) \| [ ^ > ] + > / ) ;
139+ if ( match ) {
140+ return `Email: ${ match [ 1 ] } ` ;
147141 }
148- return line ;
149- } )
150- . join ( "\n" ) ;
151- }
152-
153- // Map the author information from sentByUser if available
154- const author = msg . sentByUser
155- ? {
156- name : msg . sentByUser . name ,
157- email : msg . sentByUser . email ,
158- type : ( msg . sentByUser . isExternal ? "customer" : "user" ) as
159- | "user"
160- | "customer" ,
161142 }
162- : undefined ;
163-
164- return {
165- id : msg . id ,
166- content : content ,
167- createdAt : msg . timestamp || msg . createdAt || "" ,
168- timestamp : msg . timestamp || msg . createdAt || "" ,
169- author : author ,
170- } ;
171- } ) ;
172-
173- conversation . messages = messages ;
174- } else {
175- // Don't throw error, just leave messages empty
176- const errorText = await messagesResponse . text ( ) ;
177- console . error ( "Failed to fetch messages:" , errorText ) ;
178- conversation . messages = [ ] ;
179- }
180-
181- return { data : conversation } ;
182- } catch ( error ) {
183- return {
184- error : `Failed to fetch support ticket: ${ error instanceof Error ? error . message : "Unknown error" } ` ,
185- } ;
143+ return line ;
144+ } )
145+ . join ( "\n" ) ;
146+ }
147+
148+ // Map the author information from sentByUser if available
149+ const author = msg . sentByUser
150+ ? {
151+ name : msg . sentByUser . name ,
152+ email : msg . sentByUser . email ,
153+ type : ( msg . sentByUser . isExternal ? "customer" : "user" ) as
154+ | "user"
155+ | "customer" ,
156+ }
157+ : undefined ;
158+
159+ return {
160+ id : msg . id ,
161+ content : content ,
162+ createdAt : msg . timestamp || msg . createdAt || "" ,
163+ timestamp : msg . timestamp || msg . createdAt || "" ,
164+ author : author ,
165+ } ;
166+ } ) ;
167+
168+ conversation . messages = messages ;
169+ } else {
170+ // Don't throw error, just leave messages empty
171+ const errorText = await messagesResponse . text ( ) ;
172+ console . error ( "Failed to fetch messages:" , errorText ) ;
173+ conversation . messages = [ ] ;
186174 }
175+
176+ return conversation ;
187177}
0 commit comments