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