FeedbackDropdown.utils.ts 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136
  1. import { createSupportStorageClient } from '@/components/interfaces/Support/support-storage-client'
  2. import { generateAttachmentURLs } from '@/data/support/generate-attachment-urls-mutation'
  3. import { uuidv4 } from '@/lib/helpers'
  4. export const convertB64toBlob = (image: string) => {
  5. const contentType = 'image/png'
  6. const byteCharacters = atob(image.substr(`data:${contentType};base64,`.length))
  7. const byteArrays = []
  8. for (let offset = 0; offset < byteCharacters.length; offset += 1024) {
  9. const slice = byteCharacters.slice(offset, offset + 1024)
  10. const byteNumbers = new Array(slice.length)
  11. for (let i = 0; i < slice.length; i++) {
  12. byteNumbers[i] = slice.charCodeAt(i)
  13. }
  14. const byteArray = new Uint8Array(byteNumbers)
  15. byteArrays.push(byteArray)
  16. }
  17. const blob = new Blob(byteArrays, { type: contentType })
  18. return blob
  19. }
  20. type UploadAttachmentArgs = {
  21. image: string
  22. userId?: string
  23. }
  24. export const uploadAttachment = async ({ image, userId }: UploadAttachmentArgs) => {
  25. if (!userId) {
  26. console.error(
  27. '[FeedbackWidget > uploadAttachment] Unable to upload screenshot, missing user ID'
  28. )
  29. return undefined
  30. }
  31. const brivenClient = createSupportStorageClient()
  32. const blob = convertB64toBlob(image)
  33. const filename = `${userId}/${uuidv4()}.png`
  34. const options = { cacheControl: '3600' }
  35. const { data: file, error: uploadError } = await brivenClient.storage
  36. .from('feedback-attachments')
  37. .upload(filename, blob, options)
  38. if (uploadError || !file) {
  39. console.error('Failed to upload screenshot attachment:', uploadError)
  40. return undefined
  41. }
  42. const signedUrls = await generateAttachmentURLs({
  43. bucket: 'feedback-attachments',
  44. filenames: [file.path],
  45. })
  46. return signedUrls[0]
  47. }
  48. /**
  49. * Client-side heuristic to quickly detect obvious support requests
  50. * This provides immediate feedback before the AI classification completes
  51. */
  52. export function isLikelySupportRequest(text: string): boolean {
  53. if (!text || text.trim().length === 0) return false
  54. const lowerText = text.toLowerCase()
  55. // Common support request patterns
  56. const supportPatterns = [
  57. // Help requests
  58. /need help/i,
  59. /having trouble/i,
  60. /having issues/i,
  61. /having problems/i,
  62. /can you help/i,
  63. /could you help/i,
  64. /please help/i,
  65. /need help/i,
  66. /how do/i,
  67. /how can/i,
  68. /how to/i,
  69. /why isn't/i,
  70. /why doesn't/i,
  71. /why won't/i,
  72. /why can't/i,
  73. // Problem indicators
  74. /it's not working/i,
  75. /it doesn't work/i,
  76. /it won't work/i,
  77. /it can't work/i,
  78. /isn't working/i,
  79. /doesn't work/i,
  80. /won't work/i,
  81. /can't work/i,
  82. /not work/i,
  83. /i can't/i,
  84. /i cannot/i,
  85. /unable to/i,
  86. /wrong/i,
  87. /bad/i,
  88. // Bug/error indicators
  89. /\bbug\b/i,
  90. /\bbroken\b/i,
  91. /\berror\b/i,
  92. /\berrors\b/i,
  93. /\bfailed\b/i,
  94. /\bfailure\b/i,
  95. /\bfailing\b/i,
  96. /\bcrash\b/i,
  97. /\bcrashed\b/i,
  98. /\bcrashing\b/i,
  99. // Issue/problem keywords
  100. /\bissue\b/i,
  101. /\bissues\b/i,
  102. /\bproblem\b/i,
  103. /\bproblems\b/i,
  104. /\btrouble\b/i,
  105. /\bdifficulty\b/i,
  106. /\bdifficulties\b/i,
  107. // Support-specific phrases
  108. /support ticket/i,
  109. /contact support/i,
  110. /get help/i,
  111. /need assistance/i,
  112. /require assistance/i,
  113. /technical support/i,
  114. ]
  115. return supportPatterns.some((pattern) => pattern.test(lowerText))
  116. }