analytica-frontend-lib 1.7.20 → 1.7.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/ActivitiesHistory/index.js +6 -6
- package/dist/ActivitiesHistory/index.mjs +6 -6
- package/dist/ActivityCardQuestionBanks/index.js +6 -6
- package/dist/ActivityCardQuestionBanks/index.mjs +6 -6
- package/dist/ActivityCardQuestionPreview/index.js +6 -6
- package/dist/ActivityCardQuestionPreview/index.mjs +6 -6
- package/dist/ActivityCreate/ActivityCreateContent/index.js +6 -6
- package/dist/ActivityCreate/ActivityCreateContent/index.mjs +6 -6
- package/dist/ActivityCreate/index.js +6 -6
- package/dist/ActivityCreate/index.mjs +6 -6
- package/dist/ActivityDetails/index.js +6 -6
- package/dist/ActivityDetails/index.mjs +6 -6
- package/dist/ActivityFilters/index.js +6 -6
- package/dist/ActivityFilters/index.mjs +6 -6
- package/dist/ActivityPageLayout/index.js +6 -6
- package/dist/ActivityPageLayout/index.mjs +6 -6
- package/dist/ActivityPreview/index.js +6 -6
- package/dist/ActivityPreview/index.mjs +6 -6
- package/dist/AlertManager/index.js +6 -6
- package/dist/AlertManager/index.mjs +6 -6
- package/dist/CheckBoxGroup/index.js +6 -6
- package/dist/CheckBoxGroup/index.mjs +6 -6
- package/dist/EnemMoment/index.js +6 -6
- package/dist/EnemMoment/index.mjs +6 -6
- package/dist/EnemMomentStudentModal/index.js +6 -6
- package/dist/EnemMomentStudentModal/index.mjs +6 -6
- package/dist/ExamDetailsLayout/ExamDetailsPage/index.js +6 -6
- package/dist/ExamDetailsLayout/ExamDetailsPage/index.mjs +6 -6
- package/dist/ExamPageLayout/index.js +6 -6
- package/dist/ExamPageLayout/index.mjs +6 -6
- package/dist/KnowledgePerformanceCard/index.js +6 -6
- package/dist/KnowledgePerformanceCard/index.mjs +6 -6
- package/dist/LessonCardPreview/index.js +6 -6
- package/dist/LessonCardPreview/index.mjs +6 -6
- package/dist/LessonMediaSections/index.js +6 -6
- package/dist/LessonMediaSections/index.mjs +6 -6
- package/dist/LessonsCatalog/index.js +6 -6
- package/dist/LessonsCatalog/index.mjs +6 -6
- package/dist/PrintableUsersTable/index.js +6 -6
- package/dist/PrintableUsersTable/index.mjs +6 -6
- package/dist/QuestionsPerformanceCard/index.js +6 -6
- package/dist/QuestionsPerformanceCard/index.mjs +6 -6
- package/dist/RecommendedLessonCreate/index.js +6 -6
- package/dist/RecommendedLessonCreate/index.mjs +6 -6
- package/dist/RecommendedLessonsHistory/index.js +6 -6
- package/dist/RecommendedLessonsHistory/index.mjs +6 -6
- package/dist/SendActivityModal/SendActivityModal.js +6 -6
- package/dist/SendActivityModal/SendActivityModal.mjs +6 -6
- package/dist/SendActivityModal/index.js +6 -6
- package/dist/SendActivityModal/index.mjs +6 -6
- package/dist/SendLessonModal/index.js +6 -6
- package/dist/SendLessonModal/index.mjs +6 -6
- package/dist/SimulatedPerformance/SimulatedPerformanceView/index.js +6 -6
- package/dist/SimulatedPerformance/SimulatedPerformanceView/index.mjs +6 -6
- package/dist/SimulationsPage/index.js +6 -6
- package/dist/SimulationsPage/index.mjs +6 -6
- package/dist/Support/TicketModal/index.js +3 -3
- package/dist/Support/TicketModal/index.mjs +2 -2
- package/dist/Support/index.js +4 -4
- package/dist/Support/index.mjs +3 -3
- package/dist/SupportChatWidget/index.js +3 -3
- package/dist/SupportChatWidget/index.mjs +2 -2
- package/dist/SupportUserPanel/index.js +3 -3
- package/dist/SupportUserPanel/index.mjs +2 -2
- package/dist/TableProvider/index.js +6 -6
- package/dist/TableProvider/index.mjs +6 -6
- package/dist/UnifiedDraftModelPage/index.js +6 -6
- package/dist/UnifiedDraftModelPage/index.mjs +6 -6
- package/dist/UnifiedHistoryPage/index.js +6 -6
- package/dist/UnifiedHistoryPage/index.mjs +6 -6
- package/dist/{chunk-NP6KVOAV.js → chunk-64WFAQC6.js} +13 -2
- package/dist/chunk-64WFAQC6.js.map +1 -0
- package/dist/{chunk-56PRQ24B.mjs → chunk-7LS5UQCQ.mjs} +7 -5
- package/dist/chunk-7LS5UQCQ.mjs.map +1 -0
- package/dist/{chunk-PH2DDIS4.mjs → chunk-B4QODMQ5.mjs} +1 -1
- package/dist/{chunk-PH2DDIS4.mjs.map → chunk-B4QODMQ5.mjs.map} +1 -1
- package/dist/{chunk-TVWATS3U.mjs → chunk-C4SBX6R2.mjs} +11 -4
- package/dist/chunk-C4SBX6R2.mjs.map +1 -0
- package/dist/{chunk-FLGSHGJY.js → chunk-CYSODZGX.js} +8 -2
- package/dist/chunk-CYSODZGX.js.map +1 -0
- package/dist/{chunk-OMBS4ACE.mjs → chunk-MVT7IVMY.mjs} +12 -1
- package/dist/chunk-MVT7IVMY.mjs.map +1 -0
- package/dist/{chunk-KBWT73XY.mjs → chunk-OES54VU6.mjs} +8 -2
- package/dist/{chunk-KBWT73XY.mjs.map → chunk-OES54VU6.mjs.map} +1 -1
- package/dist/{chunk-BWTNSVJE.js → chunk-OGD2ATAP.js} +1 -1
- package/dist/{chunk-BWTNSVJE.js.map → chunk-OGD2ATAP.js.map} +1 -1
- package/dist/{chunk-DXA4VBUN.mjs → chunk-VKQ5AJDT.mjs} +2 -2
- package/dist/{chunk-HE66Y2T3.js → chunk-VQ7MSYIH.js} +5 -5
- package/dist/{chunk-HE66Y2T3.js.map → chunk-VQ7MSYIH.js.map} +1 -1
- package/dist/{chunk-IMMKWOYJ.js → chunk-WUXIPIUG.js} +42 -35
- package/dist/chunk-WUXIPIUG.js.map +1 -0
- package/dist/{chunk-SQDLSJBL.js → chunk-YRTIDZTN.js} +15 -13
- package/dist/chunk-YRTIDZTN.js.map +1 -0
- package/dist/components/Support/Support.d.ts +4 -2
- package/dist/components/Support/Support.d.ts.map +1 -1
- package/dist/components/SupportChatWidget/guestSupportChatAdapter.d.ts.map +1 -1
- package/dist/components/SupportUserPanel/SupportHistoryTab.d.ts.map +1 -1
- package/dist/components/SupportUserPanel/SupportTicketDetailsTab.d.ts.map +1 -1
- package/dist/hooks/useAppContent/index.js +6 -6
- package/dist/hooks/useAppContent/index.mjs +6 -6
- package/dist/hooks/useSupportFeatureFlag/index.js +1 -1
- package/dist/hooks/useSupportFeatureFlag/index.mjs +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +11 -7
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +10 -6
- package/dist/types/support/index.d.ts +20 -0
- package/dist/types/support/index.d.ts.map +1 -1
- package/dist/types/support/index.js +6 -2
- package/dist/types/support/index.js.map +1 -1
- package/dist/types/support/index.mjs +5 -1
- package/dist/types/supportUserPanel/index.d.ts +7 -0
- package/dist/types/supportUserPanel/index.d.ts.map +1 -1
- package/dist/types/supportUserPanel/index.js +2 -2
- package/dist/types/supportUserPanel/index.mjs +1 -1
- package/dist/types/supportUserPanel.d.ts +7 -0
- package/dist/types/supportUserPanel.d.ts.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-56PRQ24B.mjs.map +0 -1
- package/dist/chunk-FLGSHGJY.js.map +0 -1
- package/dist/chunk-IMMKWOYJ.js.map +0 -1
- package/dist/chunk-NP6KVOAV.js.map +0 -1
- package/dist/chunk-OMBS4ACE.mjs.map +0 -1
- package/dist/chunk-SQDLSJBL.js.map +0 -1
- package/dist/chunk-TVWATS3U.mjs.map +0 -1
- /package/dist/{chunk-DXA4VBUN.mjs.map → chunk-VKQ5AJDT.mjs.map} +0 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/components/SupportChatWidget/SupportChatWidget.tsx","../src/hooks/useSupportChat.ts","../src/components/SupportChatWidget/SupportChatContactForm.tsx","../src/components/SupportChatWidget/SupportChatArticleView.tsx","../src/components/SupportChatWidget/supportChatContent.tsx","../src/components/SupportChatWidget/NativeSupportChat.tsx","../src/components/SupportChatWidget/guestSupportChatAdapter.ts","../src/components/SupportChatWidget/mockSupportChatAdapter.ts"],"sourcesContent":["import {\n useCallback,\n useEffect,\n useRef,\n useState,\n type ChangeEvent,\n type KeyboardEvent,\n} from 'react';\nimport {\n ChatCircleDotsIcon,\n MinusIcon,\n PaperclipIcon,\n PaperPlaneTiltIcon,\n XIcon,\n} from '@phosphor-icons/react';\nimport Text from '../Text/Text';\nimport Button from '../Button/Button';\nimport { useSupportChat } from '../../hooks/useSupportChat';\nimport { SupportChatContactForm } from './SupportChatContactForm';\nimport { SupportChatArticleView } from './SupportChatArticleView';\nimport {\n AttachmentList,\n MessageText,\n PendingAttachment,\n SUPPORT_CHAT_ACCEPT,\n pickAttachments,\n} from './supportChatContent';\nimport { SupportType } from '../../types/support';\nimport {\n SUPPORT_CHAT_AUTHOR,\n SUPPORT_CHAT_MAX_ATTACHMENTS,\n type SupportChatAdapter,\n type SupportChatFlags,\n type SupportChatMessage,\n type SupportChatTheme,\n type SupportChatUser,\n} from '../../types/supportChat';\nimport { cn } from '../../utils/utils';\n\n/** Visibility of the chat window */\nexport type SupportChatWindowState = 'closed' | 'open' | 'minimized';\n\nexport interface SupportChatWidgetProps {\n /** Data layer (createMockSupportChatAdapter for now) */\n adapter: SupportChatAdapter;\n /** App the widget runs in, sent with the ticket (e.g. \"login\", \"backoffice\") */\n channel: string;\n institutionId?: string;\n /** Logged user; omit for visitors */\n user?: SupportChatUser;\n flags?: SupportChatFlags;\n theme?: SupportChatTheme;\n onOpen?: () => void;\n onSend?: (message: SupportChatMessage) => void;\n /**\n * Called when the window is closed with X. Closing only hides it: the\n * conversation stays connected and new replies count as unread.\n */\n onClose?: () => void;\n /**\n * Link of a FAQ article, e.g. `(id) => '/artigo/' + id`. With it, the\n * \"Baseado na FAQ\" line under an AI answer opens the article; without it\n * the line only names the article.\n */\n faqArticleUrl?: (articleId: string) => string;\n}\n\n/** Label read by screen readers on the unread badge of the launcher */\nconst formatNewMessagesLabel = (count: number): string =>\n count === 1 ? '1 nova mensagem' : `${count} novas mensagens`;\n\n/** The requester side of the conversation (user or visitor) */\nconst isOwnMessage = (message: SupportChatMessage) =>\n message.senderRole === SUPPORT_CHAT_AUTHOR.USER ||\n message.senderRole === SUPPORT_CHAT_AUTHOR.GUEST;\n\nconst formatTime = (createdAt: string) =>\n new Date(createdAt).toLocaleTimeString('pt-BR', {\n hour: '2-digit',\n minute: '2-digit',\n });\n\n/** \"Baseado na FAQ: <title>\" under an AI answer, linked when possible */\nconst FaqReference = ({\n faq,\n faqArticleUrl,\n onOpenArticle,\n}: {\n faq: { id: string; title: string };\n faqArticleUrl?: (articleId: string) => string;\n onOpenArticle?: (article: { id: string; title: string }) => void;\n}) => {\n const linked = !!onOpenArticle || !!faqArticleUrl;\n const label = (\n <Text\n as=\"span\"\n size=\"xs\"\n color=\"text-primary-700\"\n className={cn(linked && 'underline')}\n >\n Baseado na FAQ: {faq.title}\n </Text>\n );\n let content = label;\n if (onOpenArticle) {\n // Opens inside the chat, like the Zendesk messenger (no new tab)\n content = (\n <a\n href={faqArticleUrl?.(faq.id) ?? '#'}\n onClick={(event) => {\n event.preventDefault();\n onOpenArticle(faq);\n }}\n >\n {label}\n </a>\n );\n } else if (faqArticleUrl) {\n content = (\n <a href={faqArticleUrl(faq.id)} target=\"_blank\" rel=\"noreferrer\">\n {label}\n </a>\n );\n }\n return (\n <div data-testid=\"support-chat-faq\" className=\"mt-1 max-w-[80%]\">\n {content}\n </div>\n );\n};\n\nconst MessageBubble = ({\n message,\n faqArticleUrl,\n onOpenArticle,\n}: {\n message: SupportChatMessage;\n faqArticleUrl?: (articleId: string) => string;\n onOpenArticle?: (article: { id: string; title?: string }) => void;\n}) => {\n const own = isOwnMessage(message);\n return (\n <div\n className={cn(\n 'flex flex-col mb-3 transition-opacity',\n own ? 'items-end' : 'items-start',\n message.pending && 'opacity-60'\n )}\n aria-busy={message.pending || undefined}\n >\n {!own && (\n <Text size=\"xs\" className=\"text-text-500 mb-1\">\n {message.senderName}\n </Text>\n )}\n <div\n className={cn(\n 'max-w-[80%] rounded-2xl whitespace-pre-wrap break-words',\n // Image-only messages show the image without the colored bubble\n message.content\n ? cn(\n 'px-4 py-2',\n own\n ? 'bg-primary-500 text-white rounded-br-md'\n : 'bg-background-100 text-text-900 rounded-bl-md'\n )\n : 'text-text-900'\n )}\n >\n {message.content && (\n <MessageText\n text={message.content}\n onOpenArticle={\n onOpenArticle ? (id) => onOpenArticle({ id }) : undefined\n }\n />\n )}\n {message.attachments.length > 0 && (\n <AttachmentList items={message.attachments} />\n )}\n </div>\n {message.faq && (\n <FaqReference\n faq={message.faq}\n faqArticleUrl={faqArticleUrl}\n onOpenArticle={onOpenArticle}\n />\n )}\n <Text size=\"xs\" className=\"text-text-400 mt-1\">\n {message.pending ? 'Enviando…' : formatTime(message.createdAt)}\n </Text>\n </div>\n );\n};\n\n/**\n * Floating support chat (native support, the Zendesk replacement).\n * Renders nothing when disabled or when the institution uses Zendesk.\n */\nexport const SupportChatWidget = ({\n flags,\n ...props\n}: SupportChatWidgetProps) => {\n const hidden =\n flags?.enabled === false ||\n flags?.chat === false ||\n flags?.supportType === SupportType.ZENDESK;\n\n if (hidden) return null;\n return <SupportChatWidgetContent {...props} />;\n};\n\nconst SupportChatWidgetContent = ({\n adapter,\n channel,\n institutionId,\n user,\n theme,\n onOpen,\n onSend,\n onClose,\n faqArticleUrl,\n}: Omit<SupportChatWidgetProps, 'flags'>) => {\n const [windowState, setWindowState] =\n useState<SupportChatWindowState>('closed');\n const [draft, setDraft] = useState('');\n const [files, setFiles] = useState<File[]>([]);\n const [fileError, setFileError] = useState<string | null>(null);\n const [unread, setUnread] = useState(0);\n // FAQ article open inside the chat (the conversation stays as it was)\n const [article, setArticle] = useState<{ id: string; title?: string } | null>(\n null\n );\n const [expanded, setExpanded] = useState(false);\n const listRef = useRef<HTMLDivElement>(null);\n const fileInputRef = useRef<HTMLInputElement>(null);\n const seenCount = useRef(0);\n\n const { messages, sending, error, closed, send, updateContact, reset } =\n useSupportChat({\n adapter,\n channel,\n institutionId,\n user,\n });\n const [savedContactRequests, setSavedContactRequests] = useState<string[]>(\n []\n );\n\n // Latest AI request for name/e-mail: the form goes under it, and the\n // composer waits until it is sent\n const contactRequest = updateContact\n ? [...messages].reverse().find((message) => message.contactRequest?.length)\n : undefined;\n const awaitingContact =\n !!contactRequest && !savedContactRequests.includes(contactRequest.id);\n\n const isOpen = windowState === 'open';\n\n const canOpenArticle = !!adapter.getArticle && !!institutionId;\n const loadArticle = useCallback(\n (articleId: string) => {\n if (!adapter.getArticle || !institutionId) {\n return Promise.reject(new Error('Artigo indisponível'));\n }\n // Called on the adapter so a class-based adapter keeps its `this`\n return adapter.getArticle(institutionId, articleId);\n },\n [adapter, institutionId]\n );\n const openArticle = canOpenArticle\n ? (target: { id: string; title?: string }) => {\n setArticle(target);\n setExpanded(true);\n }\n : undefined;\n const closeArticle = () => {\n setArticle(null);\n setExpanded(false);\n };\n const title = theme?.title ?? 'Suporte';\n const left = theme?.position === 'bottom-left';\n const accentStyle = theme?.primaryColor\n ? { backgroundColor: theme.primaryColor }\n : undefined;\n\n // Count the support replies that arrive while the window is not visible\n useEffect(() => {\n const fresh = messages.slice(seenCount.current);\n seenCount.current = messages.length;\n if (isOpen) return;\n const replies = fresh.filter((message) => !isOwnMessage(message)).length;\n if (replies) setUnread((count) => count + replies);\n }, [messages, isOpen]);\n\n useEffect(() => {\n if (!isOpen || article) return;\n setUnread(0);\n listRef.current?.scrollTo?.({ top: listRef.current.scrollHeight });\n }, [isOpen, messages, article]);\n\n const open = () => {\n setWindowState('open');\n onOpen?.();\n };\n\n const close = () => {\n setWindowState('closed');\n onClose?.();\n };\n\n /** Drop a finished conversation so the next message opens a new ticket */\n const startOver = () => {\n setDraft('');\n setFiles([]);\n setFileError(null);\n setUnread(0);\n seenCount.current = 0;\n setSavedContactRequests([]);\n reset();\n };\n\n const submit = async () => {\n const content = draft;\n const pending = files;\n if (!content.trim() && pending.length === 0) return;\n setDraft('');\n setFiles([]);\n setFileError(null);\n const message = await send(content, pending);\n if (message) {\n onSend?.(message);\n } else {\n setDraft(content);\n setFiles(pending);\n }\n };\n\n const handleFiles = (event: ChangeEvent<HTMLInputElement>) => {\n const { accepted, problem } = pickAttachments(\n files,\n Array.from(event.target.files ?? [])\n );\n setFiles(accepted);\n setFileError(problem);\n event.target.value = '';\n };\n\n const handleKeyDown = (event: KeyboardEvent<HTMLTextAreaElement>) => {\n if (event.key === 'Enter' && !event.shiftKey) {\n event.preventDefault();\n void submit();\n }\n };\n\n return (\n <div\n className={cn(\n 'fixed bottom-4 z-50 flex flex-col gap-3',\n left ? 'left-4 items-start' : 'right-4 items-end'\n )}\n >\n {isOpen && (\n <section\n role=\"dialog\"\n aria-label={title}\n className={cn(\n 'max-w-[calc(100vw-2rem)] max-h-[calc(100vh-6rem)] flex flex-col bg-background rounded-2xl shadow-lg border border-border-100 overflow-hidden transition-[width,height]',\n expanded ? 'w-[720px] h-[615px]' : 'w-[360px] h-[520px]'\n )}\n >\n {article ? (\n <SupportChatArticleView\n articleId={article.id}\n initialTitle={article.title}\n loadArticle={loadArticle}\n expanded={expanded}\n onToggleExpanded={() => setExpanded((value) => !value)}\n onBack={closeArticle}\n externalUrl={faqArticleUrl?.(article.id)}\n accentStyle={accentStyle}\n />\n ) : (\n <>\n <header\n className=\"flex items-center justify-between px-4 py-3 bg-primary-500 text-white\"\n style={accentStyle}\n >\n <Text size=\"md\" weight=\"semibold\" color=\"\">\n {title}\n </Text>\n <div className=\"flex items-center gap-1\">\n <Button\n variant=\"raw\"\n aria-label=\"Minimizar\"\n onClick={() => setWindowState('minimized')}\n className=\"p-1 rounded hover:bg-white/20\"\n >\n <MinusIcon size={18} />\n </Button>\n <Button\n variant=\"raw\"\n aria-label=\"Fechar\"\n onClick={close}\n className=\"p-1 rounded hover:bg-white/20\"\n >\n <XIcon size={18} />\n </Button>\n </div>\n </header>\n\n <div ref={listRef} className=\"flex-1 overflow-y-auto px-4 py-3\">\n {messages.length === 0 ? (\n <Text size=\"sm\" className=\"text-text-500 text-center mt-8\">\n Olá! Como podemos te ajudar?\n </Text>\n ) : (\n messages.map((message) => (\n <div key={message.id}>\n <MessageBubble\n message={message}\n faqArticleUrl={faqArticleUrl}\n onOpenArticle={openArticle}\n />\n {updateContact &&\n message.id === contactRequest?.id &&\n message.contactRequest && (\n <SupportChatContactForm\n fields={message.contactRequest}\n accentColor={theme?.primaryColor}\n onSubmit={async (contact) => {\n const ok = await updateContact(contact);\n if (ok) {\n setSavedContactRequests((ids) => [\n ...ids,\n message.id,\n ]);\n }\n return ok;\n }}\n />\n )}\n </div>\n ))\n )}\n </div>\n\n {(fileError || error) && (\n <Text\n size=\"xs\"\n role=\"alert\"\n className=\"px-4 py-2 text-error-700 bg-error-50\"\n >\n {fileError ?? error}\n </Text>\n )}\n\n {closed ? (\n <div className=\"flex flex-col items-center gap-2 px-4 py-3 border-t border-border-100\">\n <Text size=\"sm\" color=\"text-text-500\" className=\"text-center\">\n Este atendimento foi encerrado.\n </Text>\n <Button variant=\"link\" size=\"small\" onClick={startOver}>\n Iniciar nova conversa\n </Button>\n </div>\n ) : (\n <form\n className=\"flex flex-col gap-2 px-3 py-3 border-t border-border-100\"\n onSubmit={(event) => {\n event.preventDefault();\n void submit();\n }}\n >\n {files.length > 0 && (\n <div className=\"flex flex-wrap gap-2\">\n {files.map((file, index) => (\n <PendingAttachment\n key={`${file.name}-${index}`}\n file={file}\n onRemove={() =>\n setFiles((list) =>\n list.filter((_, i) => i !== index)\n )\n }\n />\n ))}\n </div>\n )}\n <div className=\"flex items-end gap-2\">\n <input\n ref={fileInputRef}\n type=\"file\"\n accept={SUPPORT_CHAT_ACCEPT}\n multiple\n hidden\n data-testid=\"support-chat-file-input\"\n onChange={handleFiles}\n />\n <Button\n variant=\"raw\"\n aria-label=\"Anexar arquivo\"\n disabled={\n sending ||\n awaitingContact ||\n files.length >= SUPPORT_CHAT_MAX_ATTACHMENTS\n }\n onClick={() => fileInputRef.current?.click()}\n className=\"p-2 rounded-full text-text-600 hover:bg-background-100 disabled:opacity-50\"\n >\n <PaperclipIcon size={20} />\n </Button>\n <textarea\n aria-label=\"Mensagem\"\n placeholder={\n awaitingContact\n ? 'Preencha seus dados acima'\n : 'Digite sua mensagem'\n }\n disabled={awaitingContact}\n rows={1}\n value={draft}\n onChange={(event) => setDraft(event.target.value)}\n onKeyDown={handleKeyDown}\n className=\"flex-1 resize-none rounded-xl border border-border-300 px-3 py-2 text-sm text-text-900 focus:outline-none focus:border-primary-500 max-h-28\"\n />\n <Button\n variant=\"raw\"\n type=\"submit\"\n aria-label=\"Enviar\"\n disabled={\n sending ||\n awaitingContact ||\n (!draft.trim() && files.length === 0)\n }\n className=\"p-2 rounded-full bg-primary-500 text-white disabled:opacity-50\"\n style={accentStyle}\n >\n <PaperPlaneTiltIcon size={18} />\n </Button>\n </div>\n </form>\n )}\n </>\n )}\n </section>\n )}\n\n {!isOpen && (\n <Button\n variant=\"raw\"\n aria-label={`Abrir ${title.toLowerCase()}`}\n onClick={open}\n className=\"relative w-14 h-14 rounded-full bg-primary-500 text-white shadow-lg flex items-center justify-center hover:brightness-110\"\n style={accentStyle}\n >\n <ChatCircleDotsIcon size={28} />\n {unread > 0 && (\n <Text\n as=\"span\"\n size=\"xs\"\n color=\"text-white\"\n aria-label={formatNewMessagesLabel(unread)}\n className=\"absolute -top-1 -right-1 min-w-5 h-5 px-1 rounded-full bg-error-500 flex items-center justify-center\"\n >\n {unread}\n </Text>\n )}\n </Button>\n )}\n </div>\n );\n};\n","import { useCallback, useEffect, useRef, useState } from 'react';\nimport {\n SUPPORT_CHAT_AUTHOR,\n SUPPORT_CHAT_GUEST_SENDER_ID,\n SUPPORT_CHAT_STATUS,\n type SupportChatAdapter,\n type SupportChatContact,\n type SupportChatEvent,\n type SupportChatMessage,\n type SupportChatTicket,\n type SupportChatUser,\n} from '../types/supportChat';\n\nexport interface UseSupportChatConfig {\n adapter: SupportChatAdapter;\n channel: string;\n institutionId?: string;\n user?: SupportChatUser;\n}\n\nexport interface UseSupportChatReturn {\n ticket: SupportChatTicket | null;\n messages: SupportChatMessage[];\n sending: boolean;\n error: string | null;\n /** Ticket resolved: no more messages can be sent */\n closed: boolean;\n /** Send a message (text and/or files); the first one opens the ticket */\n send: (content: string, files?: File[]) => Promise<SupportChatMessage | null>;\n /**\n * Save the contact typed in the form shown under the AI request. Resolves\n * false (and sets `error`) when it fails. Undefined when the adapter cannot\n * save it.\n */\n updateContact?: (contact: SupportChatContact) => Promise<boolean>;\n /** Forget the current conversation */\n reset: () => void;\n}\n\nlet pendingSequence = 0;\n\n/**\n * Message shown right away while it is being sent (optimistic rendering).\n * Images use local object URLs, released when the real message arrives.\n */\nconst buildPendingMessage = (\n content: string,\n files: File[],\n user?: SupportChatUser\n): SupportChatMessage => {\n pendingSequence += 1;\n return {\n id: `pending-${pendingSequence}`,\n senderId: SUPPORT_CHAT_GUEST_SENDER_ID,\n senderName: user?.name || 'Você',\n senderPhoto: null,\n senderRole: user?.email\n ? SUPPORT_CHAT_AUTHOR.USER\n : SUPPORT_CHAT_AUTHOR.GUEST,\n content,\n messageType: 'text',\n createdAt: new Date().toISOString(),\n attachments: files.map((file, index) => ({\n id: `pending-${pendingSequence}-${index}`,\n url: URL.createObjectURL(file),\n fileName: file.name,\n mimeType: file.type,\n size: file.size,\n })),\n pending: true,\n };\n};\n\nconst releasePending = (message: SupportChatMessage) =>\n message.attachments.forEach((item) => URL.revokeObjectURL(item.url));\n\nconst getErrorMessage = (error: unknown) =>\n error instanceof Error ? error.message : 'Não foi possível enviar a mensagem';\n\n/** Add or replace a message, keeping the list ordered by arrival */\nconst upsertMessage = (\n list: SupportChatMessage[],\n message: SupportChatMessage\n): SupportChatMessage[] =>\n list.some((item) => item.id === message.id)\n ? list.map((item) => (item.id === message.id ? message : item))\n : [...list, message];\n\n/**\n * State of a support conversation on top of a SupportChatAdapter.\n * The REST-like calls write; the subscription only applies the events,\n * so a message received both ways is stored once.\n */\nexport const useSupportChat = ({\n adapter,\n channel,\n institutionId,\n user,\n}: UseSupportChatConfig): UseSupportChatReturn => {\n const [ticket, setTicket] = useState<SupportChatTicket | null>(null);\n const [messages, setMessages] = useState<SupportChatMessage[]>([]);\n const [sending, setSending] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const sendingRef = useRef(false);\n\n const ticketId = ticket?.id;\n\n useEffect(() => {\n if (!ticketId) return;\n\n const handleEvent = (event: SupportChatEvent) => {\n switch (event.type) {\n case 'new_message':\n setMessages((list) => upsertMessage(list, event.payload.message));\n break;\n case 'message_updated':\n setMessages((list) =>\n list.map((item) =>\n item.id === event.payload.messageId\n ? { ...item, content: event.payload.content }\n : item\n )\n );\n break;\n case 'ticket_updated': {\n const { ticketId: id, ...rest } = event.payload;\n setTicket((current) =>\n current?.id === id ? { ...current, ...rest } : current\n );\n break;\n }\n case 'error':\n setError(event.payload.message);\n break;\n }\n };\n\n return adapter.subscribe(ticketId, handleEvent);\n }, [adapter, ticketId]);\n\n const send = useCallback(\n async (content: string, files: File[] = []) => {\n const text = content.trim();\n if ((!text && files.length === 0) || sendingRef.current) return null;\n\n sendingRef.current = true;\n setSending(true);\n setError(null);\n\n const pending = buildPendingMessage(text, files, user);\n setMessages((list) => [...list, pending]);\n // Swap the optimistic message for the confirmed ones\n const confirm = (confirmed: SupportChatMessage[]) =>\n setMessages((list) =>\n confirmed.reduce(\n upsertMessage,\n list.filter((item) => item.id !== pending.id)\n )\n );\n\n try {\n if (ticketId) {\n const message = await adapter.sendMessage(ticketId, text, files);\n confirm([message]);\n return message;\n }\n\n const started = await adapter.startConversation({\n message: text,\n channel,\n institutionId,\n requesterName: user?.name,\n email: user?.email,\n files,\n });\n setTicket(started.ticket);\n confirm(started.messages);\n return started.messages[started.messages.length - 1] ?? null;\n } catch (sendError) {\n setMessages((list) => list.filter((item) => item.id !== pending.id));\n setError(getErrorMessage(sendError));\n return null;\n } finally {\n releasePending(pending);\n sendingRef.current = false;\n setSending(false);\n }\n },\n [adapter, channel, institutionId, ticketId, user]\n );\n\n const updateContact = useCallback(\n async (contact: SupportChatContact) => {\n if (!ticketId || !adapter.updateContact) return false;\n setError(null);\n try {\n await adapter.updateContact(ticketId, contact);\n return true;\n } catch (contactError) {\n setError(\n (contactError as { response?: { status?: number } } | null)?.response\n ?.status === 409\n ? 'O e-mail deste atendimento já foi informado'\n : 'Não foi possível salvar seus dados. Tente de novo.'\n );\n return false;\n }\n },\n [adapter, ticketId]\n );\n\n const reset = useCallback(() => {\n setTicket(null);\n setMessages([]);\n setError(null);\n }, []);\n\n return {\n ticket,\n messages,\n sending,\n error,\n closed: ticket?.status === SUPPORT_CHAT_STATUS.FECHADO,\n send,\n updateContact: adapter.updateContact ? updateContact : undefined,\n reset,\n };\n};\n","import { useState, type FormEvent } from 'react';\nimport Text from '../Text/Text';\nimport Button from '../Button/Button';\nimport Input from '../Input/Input';\nimport type {\n SupportChatContact,\n SupportChatContactField,\n} from '../../types/supportChat';\n\nexport interface SupportChatContactFormProps {\n /** Fields the AI asked for, in order (one step each) */\n fields: SupportChatContactField[];\n /** Saves the contact; resolves true when it worked */\n onSubmit: (contact: SupportChatContact) => Promise<boolean>;\n /** Accent color of the theme, for the button */\n accentColor?: string;\n}\n\nconst STEPS: Record<\n SupportChatContactField,\n { label: string; type: string; autoComplete: string; placeholder: string }\n> = {\n name: {\n label: 'Seu nome',\n type: 'text',\n autoComplete: 'name',\n placeholder: 'Como podemos te chamar?',\n },\n email: {\n label: 'Seu e-mail',\n type: 'email',\n autoComplete: 'email',\n placeholder: 'nome@exemplo.com',\n },\n};\n\nconst EMAIL_PATTERN = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/;\n\n/** Problem with a typed value, or null when it can be sent */\nconst validate = (\n field: SupportChatContactField,\n value: string\n): string | null => {\n if (field === 'name') {\n return value.length >= 2 ? null : 'Digite seu nome';\n }\n return EMAIL_PATTERN.test(value) ? null : 'Digite um e-mail válido';\n};\n\n/**\n * Step-by-step form for the contact the AI asks on the first contact (name,\n * then e-mail), shown under the AI message like the Zendesk pre-chat form.\n * The values go to the backend as fields, not as a chat message. Once sent,\n * it shows what was saved.\n */\nexport const SupportChatContactForm = ({\n fields,\n onSubmit,\n accentColor,\n}: SupportChatContactFormProps) => {\n const [step, setStep] = useState(0);\n const [values, setValues] = useState<Record<string, string>>({});\n const [problem, setProblem] = useState<string | null>(null);\n const [saving, setSaving] = useState(false);\n const [saved, setSaved] = useState(false);\n\n const field = fields[step];\n const config = STEPS[field];\n const value = values[field] ?? '';\n const isLast = step === fields.length - 1;\n\n if (saved) {\n return (\n <div\n data-testid=\"support-chat-contact-saved\"\n className=\"mb-3 ml-1 flex max-w-[85%] flex-col gap-1 rounded-2xl border border-border-100 px-4 py-3\"\n >\n {fields.map((item) => (\n <Text key={item} size=\"xs\" color=\"text-text-600\">\n {STEPS[item].label}:{' '}\n <Text as=\"span\" size=\"xs\" weight=\"semibold\" color=\"text-text-900\">\n {values[item]}\n </Text>\n </Text>\n ))}\n </div>\n );\n }\n\n const advance = async (event: FormEvent) => {\n event.preventDefault();\n const typed = field === 'email' ? value.trim().toLowerCase() : value.trim();\n const error = validate(field, typed);\n setProblem(error);\n if (error) return;\n\n const next = { ...values, [field]: typed };\n setValues(next);\n if (!isLast) {\n setStep(step + 1);\n return;\n }\n\n setSaving(true);\n const ok = await onSubmit({\n ...(next.name ? { requesterName: next.name } : {}),\n ...(next.email ? { email: next.email } : {}),\n });\n setSaving(false);\n if (ok) setSaved(true);\n };\n\n const inputId = `support-chat-contact-${field}`;\n\n return (\n <form\n aria-label=\"Seus dados\"\n noValidate\n onSubmit={(event) => void advance(event)}\n className=\"mb-3 ml-1 flex max-w-[85%] flex-col gap-3 rounded-2xl border border-border-100 px-4 py-3\"\n >\n <Input\n id={inputId}\n key={field}\n label={config.label}\n variant=\"rounded\"\n size=\"small\"\n type={config.type}\n autoComplete={config.autoComplete}\n placeholder={config.placeholder}\n value={value}\n autoFocus\n disabled={saving}\n errorMessage={problem ?? undefined}\n onChange={(event) =>\n setValues((current) => ({ ...current, [field]: event.target.value }))\n }\n />\n <div className=\"flex items-center justify-between\">\n <Text size=\"xs\" color=\"text-text-500\">\n {fields.length > 1 ? `${step + 1} de ${fields.length}` : ''}\n </Text>\n <Button\n type=\"submit\"\n size=\"small\"\n disabled={saving}\n style={accentColor ? { backgroundColor: accentColor } : undefined}\n >\n {isLast ? 'Enviar' : 'Avançar'}\n </Button>\n </div>\n </form>\n );\n};\n","import { useEffect, useMemo, useRef, useState } from 'react';\nimport {\n ArrowLeftIcon,\n ArrowSquareOutIcon,\n ArrowsInSimpleIcon,\n ArrowsOutSimpleIcon,\n} from '@phosphor-icons/react';\nimport Text from '../Text/Text';\nimport Button from '../Button/Button';\nimport { ArticleContent } from '../ArticleContent/ArticleContent';\nimport { embedVideoLinks } from '../../utils/videoEmbed';\nimport type { SupportChatArticle } from '../../types/supportChat';\nimport { cn } from '../../utils/utils';\n\nexport interface SupportChatArticleViewProps {\n /** Article to show */\n articleId: string;\n /** Title known from the chat, shown while the article loads */\n initialTitle?: string;\n /** Loads the article (adapter.getArticle bound to the institution) */\n loadArticle: (articleId: string) => Promise<SupportChatArticle>;\n /** Whether the chat window is in the wide mode */\n expanded: boolean;\n onToggleExpanded: () => void;\n /** Back to the conversation (it is kept as it was) */\n onBack: () => void;\n /** Page of the article in the help center, for \"Abrir em nova aba\" */\n externalUrl?: string;\n /** Header background (theme) */\n accentStyle?: { backgroundColor: string };\n}\n\nconst formatUpdatedAt = (iso: string) =>\n new Date(iso).toLocaleDateString('pt-BR', {\n day: '2-digit',\n month: '2-digit',\n year: 'numeric',\n });\n\n/**\n * FAQ article read inside the support chat, like the Zendesk messenger: the\n * conversation is replaced by the article (no new tab), with back,\n * expand/reduce and open-in-new-tab controls. The big title moves to the\n * header once it scrolls out of view. YouTube/Vimeo links of the article\n * become players.\n */\nexport const SupportChatArticleView = ({\n articleId,\n initialTitle,\n loadArticle,\n expanded,\n onToggleExpanded,\n onBack,\n externalUrl,\n accentStyle,\n}: SupportChatArticleViewProps) => {\n const [article, setArticle] = useState<SupportChatArticle | null>(null);\n const [failed, setFailed] = useState(false);\n const [titleHidden, setTitleHidden] = useState(false);\n const titleRef = useRef<HTMLHeadingElement>(null);\n\n useEffect(() => {\n let active = true;\n setArticle(null);\n setFailed(false);\n loadArticle(articleId)\n .then((loaded) => {\n if (active) setArticle(loaded);\n })\n .catch(() => {\n if (active) setFailed(true);\n });\n return () => {\n active = false;\n };\n }, [articleId, loadArticle]);\n\n // The big title leaves the screen → show it in the header\n useEffect(() => {\n const title = titleRef.current;\n if (!title || typeof IntersectionObserver === 'undefined') {\n return undefined;\n }\n const observer = new IntersectionObserver(([entry]) =>\n setTitleHidden(!entry.isIntersecting)\n );\n observer.observe(title);\n return () => observer.disconnect();\n }, [article]);\n\n const html = useMemo(\n () => (article ? embedVideoLinks(article.body) : ''),\n [article]\n );\n const title = article?.title ?? initialTitle ?? 'Artigo';\n\n return (\n <div\n data-testid=\"support-chat-article\"\n className=\"flex min-h-0 flex-1 flex-col\"\n >\n <header\n className=\"flex items-center gap-1 px-2 py-2 bg-primary-500 text-white\"\n style={accentStyle}\n >\n <Button\n variant=\"raw\"\n aria-label=\"Voltar para a janela de mensagens\"\n onClick={onBack}\n className=\"p-1 rounded hover:bg-white/20\"\n >\n <ArrowLeftIcon size={18} />\n </Button>\n <Text\n as=\"span\"\n size=\"sm\"\n weight=\"semibold\"\n color=\"\"\n aria-hidden={!titleHidden || undefined}\n className={cn(\n 'min-w-0 flex-1 truncate transition-opacity',\n titleHidden ? 'opacity-100' : 'opacity-0'\n )}\n >\n {title}\n </Text>\n <Button\n variant=\"raw\"\n aria-label={expanded ? 'Reduzir artigo' : 'Expandir artigo'}\n onClick={onToggleExpanded}\n className=\"p-1 rounded hover:bg-white/20\"\n >\n {expanded ? (\n <ArrowsInSimpleIcon size={18} />\n ) : (\n <ArrowsOutSimpleIcon size={18} />\n )}\n </Button>\n {externalUrl && (\n <a\n href={externalUrl}\n target=\"_blank\"\n rel=\"noreferrer\"\n aria-label=\"Abrir em nova aba\"\n className=\"p-1 rounded hover:bg-white/20\"\n >\n <ArrowSquareOutIcon size={18} />\n </a>\n )}\n </header>\n\n <div className=\"flex-1 overflow-y-auto px-5 py-4\">\n <Text\n as=\"h2\"\n ref={titleRef}\n size=\"xl\"\n weight=\"bold\"\n color=\"text-text-950\"\n >\n {title}\n </Text>\n {article?.updatedAt && (\n <Text size=\"xs\" color=\"text-text-500\" className=\"mt-1\">\n Atualizado em {formatUpdatedAt(article.updatedAt)}\n </Text>\n )}\n\n <div className=\"mt-4\">\n {failed && (\n <Text size=\"sm\" role=\"alert\" color=\"text-error-700\">\n Não foi possível abrir o artigo.\n </Text>\n )}\n {!failed && !article && (\n <Text size=\"sm\" color=\"text-text-500\">\n Carregando…\n </Text>\n )}\n {article && <ArticleContent html={html} />}\n </div>\n </div>\n </div>\n );\n};\n","import { useEffect, useState, type ReactNode } from 'react';\nimport {\n FileIcon,\n FilePdfIcon,\n FileVideoIcon,\n XIcon,\n} from '@phosphor-icons/react';\nimport Text from '../Text/Text';\nimport Button from '../Button/Button';\nimport {\n SUPPORT_CHAT_ATTACHMENT_RULES,\n SUPPORT_CHAT_MAX_ATTACHMENTS,\n type SupportChatAttachment,\n} from '../../types/supportChat';\n\n/** Value of the `accept` attribute of the file picker */\nexport const SUPPORT_CHAT_ACCEPT = Object.keys(\n SUPPORT_CHAT_ATTACHMENT_RULES\n).join(',');\n\nconst isImage = (mimeType?: string) => Boolean(mimeType?.startsWith('image/'));\nconst isVideo = (mimeType?: string) => Boolean(mimeType?.startsWith('video/'));\n\nconst formatSize = (bytes: number) => `${Math.round(bytes / (1024 * 1024))} MB`;\n\n/**\n * Keep the files the backend accepts (type, size and count) and say why the\n * others were left out.\n * @param current - Files already picked\n * @param picked - Files just chosen\n * @returns The accepted files and the last problem found, if any\n */\nexport const pickAttachments = (current: File[], picked: File[]) => {\n const accepted = [...current];\n let problem: string | null = null;\n for (const file of picked) {\n const maxBytes = SUPPORT_CHAT_ATTACHMENT_RULES[file.type];\n if (!maxBytes) {\n problem =\n 'Envie imagens (JPG, PNG, GIF, WEBP), PDF ou vídeo (MP4, WEBM, MOV)';\n } else if (file.size > maxBytes) {\n problem = `${file.name} passa de ${formatSize(maxBytes)}`;\n } else if (accepted.length >= SUPPORT_CHAT_MAX_ATTACHMENTS) {\n problem = `Máximo de ${SUPPORT_CHAT_MAX_ATTACHMENTS} arquivos por mensagem`;\n } else {\n accepted.push(file);\n }\n }\n return { accepted, problem };\n};\n\nconst FileTypeIcon = ({ mimeType }: { mimeType?: string }) => {\n if (mimeType === 'application/pdf') return <FilePdfIcon size={18} />;\n if (isVideo(mimeType)) return <FileVideoIcon size={18} />;\n return <FileIcon size={18} />;\n};\n\n/** Files of a message: images and videos inline, other files as links */\nexport const AttachmentList = ({\n items,\n}: {\n items: SupportChatAttachment[];\n}) => (\n <div className=\"flex flex-wrap gap-2 first:mt-0 mt-1\">\n {items.map((item) => {\n if (isImage(item.mimeType)) {\n return (\n <a key={item.id} href={item.url} target=\"_blank\" rel=\"noreferrer\">\n <img\n src={item.url}\n alt={item.fileName}\n className=\"max-w-[200px] max-h-[200px] rounded-lg object-cover\"\n />\n </a>\n );\n }\n if (isVideo(item.mimeType)) {\n return (\n <video\n key={item.id}\n src={item.url}\n controls\n preload=\"metadata\"\n aria-label={item.fileName}\n className=\"max-w-[240px] max-h-[200px] rounded-lg bg-black\"\n />\n );\n }\n return (\n <a\n key={item.id}\n href={item.url}\n target=\"_blank\"\n rel=\"noreferrer\"\n className=\"flex items-center gap-1 underline\"\n >\n <FileTypeIcon mimeType={item.mimeType} />\n <Text as=\"span\" size=\"sm\" color=\"\">\n {item.fileName}\n </Text>\n </a>\n );\n })}\n </div>\n);\n\n/** A file picked but not sent yet: thumbnail for images, name for the rest */\nexport const PendingAttachment = ({\n file,\n onRemove,\n}: {\n file: File;\n onRemove: () => void;\n}) => {\n const image = isImage(file.type);\n const [url, setUrl] = useState('');\n useEffect(() => {\n if (!image) return undefined;\n const objectUrl = URL.createObjectURL(file);\n setUrl(objectUrl);\n return () => URL.revokeObjectURL(objectUrl);\n }, [file, image]);\n\n return (\n <div className=\"relative\">\n {image ? (\n url && (\n <img\n src={url}\n alt={file.name}\n className=\"w-14 h-14 rounded-lg object-cover border border-border-100\"\n />\n )\n ) : (\n <div className=\"flex h-14 max-w-[160px] items-center gap-1 rounded-lg border border-border-100 px-2 text-text-700\">\n <FileTypeIcon mimeType={file.type} />\n <Text as=\"span\" size=\"xs\" className=\"truncate\">\n {file.name}\n </Text>\n </div>\n )}\n <Button\n variant=\"raw\"\n aria-label={`Remover ${file.name}`}\n onClick={onRemove}\n className=\"absolute -top-1 -right-1 w-5 h-5 rounded-full bg-text-700 text-white flex items-center justify-center\"\n >\n <XIcon size={12} />\n </Button>\n </div>\n );\n};\n\n/** http(s) addresses inside a message */\nconst URL_PATTERN = /https?:\\/\\/[^\\s<>\"]+/g;\n\n/** Punctuation that ends a sentence, not the address */\nconst TRAILING_PUNCTUATION = /[.,;:!?)\\]]+$/;\n\n/** A FAQ article page: `/artigo/<uuid>` */\nconst ARTICLE_PATH =\n /\\/artigo\\/([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})/i;\n\n/**\n * Message text with its addresses as links. A link to a FAQ article opens in\n * the chat (when `onOpenArticle` is given); the others open a new tab.\n */\nexport const MessageText = ({\n text,\n onOpenArticle,\n}: {\n text: string;\n onOpenArticle?: (articleId: string) => void;\n}) => {\n const parts: ReactNode[] = [];\n let cursor = 0;\n for (const match of text.matchAll(URL_PATTERN)) {\n const raw = match[0];\n const url = raw.replace(TRAILING_PUNCTUATION, '');\n const start = match.index ?? 0;\n if (start > cursor) parts.push(text.slice(cursor, start));\n\n const articleId = onOpenArticle ? ARTICLE_PATH.exec(url)?.[1] : undefined;\n parts.push(\n <a\n key={`${start}-${url}`}\n href={url}\n target=\"_blank\"\n rel=\"noreferrer\"\n className=\"underline break-all\"\n onClick={\n articleId\n ? (event) => {\n event.preventDefault();\n onOpenArticle?.(articleId);\n }\n : undefined\n }\n >\n {url}\n </a>\n );\n cursor = start + url.length;\n }\n if (cursor < text.length) parts.push(text.slice(cursor));\n\n return (\n <Text size=\"sm\" color=\"\" className=\"whitespace-pre-wrap\">\n {parts}\n </Text>\n );\n};\n","import { useMemo } from 'react';\nimport { SupportChatWidget } from './SupportChatWidget';\nimport { createGuestSupportChatAdapter } from './guestSupportChatAdapter';\nimport { useSupportFeatureFlag } from '../../hooks/useSupportFeatureFlag';\nimport { useAppStore } from '../../store/appStore';\nimport { useAuthStore } from '../../store/authStore';\nimport { SupportType, type SupportApiClient } from '../../types/support';\nimport type {\n SupportChatHttpClient,\n SupportChatTheme,\n} from '../../types/supportChat';\n\nexport interface NativeSupportChatProps {\n /** Client of the backend API (the app axios instance fits as it is) */\n apiClient: SupportApiClient & SupportChatHttpClient;\n /** App the chat runs in, sent with the ticket (e.g. \"aluno\", \"login\") */\n channel: string;\n /**\n * Extra condition of the app to show the chat, on top of the SUPPORT flag\n * (e.g. logged in and with the support module). Default: true.\n */\n enabled?: boolean;\n /**\n * Link of a FAQ article. Default: the FAQ address of the SUPPORT flag\n * (`<faqUrl>/artigo/<id>`).\n */\n faqArticleUrl?: (articleId: string) => string;\n /** Support WebSocket; without it the chat polls the REST API */\n wsUrl?: string;\n theme?: SupportChatTheme;\n}\n\n/**\n * Native support chat ready to drop in an app: reads the SUPPORT feature flag\n * of the institution (from the app store), the logged user (from the auth\n * store) and talks to the support API by itself.\n *\n * Shown only when the institution uses the native support with the internal\n * support and its chat switched on in the backoffice, once the flag is read,\n * and when `enabled`. With Zendesk it renders nothing (the app loads the\n * Zendesk widget). A logged user opens the ticket with their name and e-mail;\n * a visitor gives them to the AI on the first contact.\n *\n * @example\n * <NativeSupportChat apiClient={api} channel=\"aluno\" enabled={hasSupport} />\n */\nexport const NativeSupportChat = ({\n apiClient,\n channel,\n enabled = true,\n faqArticleUrl,\n wsUrl,\n theme,\n}: NativeSupportChatProps) => {\n const { institutionId } = useAppStore();\n const userName = useAuthStore(\n (state) => state.user?.name ?? state.sessionInfo?.userName\n );\n const userEmail = useAuthStore((state) => state.user?.email);\n const flag = useSupportFeatureFlag({ apiClient });\n const adapter = useMemo(\n () => createGuestSupportChatAdapter({ apiClient, wsUrl }),\n [apiClient, wsUrl]\n );\n\n const user =\n userName || userEmail ? { name: userName, email: userEmail } : undefined;\n\n return (\n <SupportChatWidget\n adapter={adapter}\n channel={channel}\n institutionId={institutionId || undefined}\n user={user}\n theme={theme}\n faqArticleUrl={faqArticleUrl ?? flag.faqArticleUrl}\n flags={{\n enabled: enabled && !flag.loading && flag.isNative && flag.chatEnabled,\n supportType: flag.isNative ? SupportType.NATIVE : SupportType.ZENDESK,\n }}\n />\n );\n};\n","import {\n SUPPORT_CHAT_AUTHOR,\n SUPPORT_CHAT_GUEST_SENDER_ID,\n SUPPORT_CHAT_STATUS,\n type SupportChatAdapter,\n type SupportChatAnswerApi,\n type SupportChatArticle,\n type SupportChatEvent,\n type SupportChatHttpClient,\n type SupportChatMessage,\n} from '../../types/supportChat';\n\nexport interface GuestSupportChatAdapterOptions {\n /** Client of the backend API (e.g. the app axios instance) */\n apiClient: SupportChatHttpClient;\n /**\n * Support WebSocket, including ?channel=support (e.g.\n * wss://bff-hml-chat.analyticaensino.com.br?channel=support).\n * Without it the adapter only polls the REST API.\n */\n wsUrl?: string;\n /** Polling interval while the socket is not connected, in ms */\n pollIntervalMs?: number;\n}\n\nconst GUEST_TOKEN_HEADER = 'x-support-guest-token';\n\nconst senderNameFor = (role: string, requesterName: string) => {\n if (role === SUPPORT_CHAT_AUTHOR.AGENT) return 'Suporte';\n if (role === SUPPORT_CHAT_AUTHOR.AI) return 'Assistente';\n return requesterName;\n};\n\n/** REST answer → widget message */\nexport const fromSupportAnswer = (\n answer: SupportChatAnswerApi,\n requesterName: string\n): SupportChatMessage => ({\n id: answer.id,\n senderId:\n answer.authorType === SUPPORT_CHAT_AUTHOR.GUEST\n ? SUPPORT_CHAT_GUEST_SENDER_ID\n : answer.authorType.toLowerCase(),\n senderName: senderNameFor(answer.authorType, requesterName),\n senderPhoto: null,\n senderRole: answer.authorType,\n content: answer.answer,\n messageType: 'text',\n createdAt: answer.createdAt,\n attachments: answer.attachments ?? [],\n faq:\n answer.faqArticleId && answer.faqArticleTitle\n ? { id: answer.faqArticleId, title: answer.faqArticleTitle }\n : null,\n contactRequest: answer.contactRequest ?? null,\n});\n\n/**\n * Support chat adapter for visitors (no login), on top of the support API:\n * - POST /support/external opens the ticket and returns the guest token;\n * - GET/POST /support/guest/:id/answer read and send, with the token header;\n * - the chat WebSocket (?channel=support&roomId=&token=) pushes the events,\n * and REST polling covers the time the socket is down.\n */\nexport const createGuestSupportChatAdapter = ({\n apiClient,\n wsUrl,\n pollIntervalMs = 3000,\n}: GuestSupportChatAdapterOptions): SupportChatAdapter => {\n const sessions = new Map<string, { token: string; name: string }>();\n /**\n * Ticket opening text written by the adapter (an image-only start still\n * needs a description). It is the ticket description for the agents, not\n * something the visitor typed, so the chat does not show it.\n */\n const hiddenIds = new Set<string>();\n const isVisible = (message: SupportChatMessage) => !hiddenIds.has(message.id);\n\n const headersFor = (ticketId: string) => ({\n [GUEST_TOKEN_HEADER]: sessions.get(ticketId)?.token ?? '',\n });\n const nameFor = (ticketId: string) =>\n sessions.get(ticketId)?.name ?? 'Visitante';\n\n const listMessages = async (ticketId: string) => {\n const { data } = await apiClient.get<{ data: SupportChatAnswerApi[] }>(\n `/support/guest/${ticketId}/answer`,\n { headers: headersFor(ticketId) }\n );\n return data.data\n .map((answer) => fromSupportAnswer(answer, nameFor(ticketId)))\n .filter(isVisible);\n };\n\n /**\n * Upload each file straight to the storage: ask the backend for a signed\n * URL, then PUT the file there. Returns what the message needs.\n */\n const uploadFiles = (ticketId: string, files: File[]) =>\n Promise.all(\n files.map(async (file) => {\n const { data } = await apiClient.post<{\n data: { uploadUrl: string; key: string; contentType: string };\n }>(\n `/support/guest/${ticketId}/upload-url`,\n { fileName: file.name, fileSize: file.size, mimeType: file.type },\n { headers: headersFor(ticketId) }\n );\n const { uploadUrl, key, contentType } = data.data;\n const response = await fetch(uploadUrl, {\n method: 'PUT',\n headers: { 'Content-Type': contentType },\n body: file,\n });\n if (!response.ok) {\n throw new Error(`Não foi possível enviar ${file.name}`);\n }\n return {\n key,\n fileName: file.name,\n mimeType: contentType,\n size: file.size,\n };\n })\n );\n\n const sendMessage = async (\n ticketId: string,\n content: string,\n files: File[] = []\n ) => {\n const attachments = await uploadFiles(ticketId, files);\n const { data } = await apiClient.post<{ data: SupportChatAnswerApi }>(\n `/support/guest/${ticketId}/answer`,\n { answer: content, attachments },\n { headers: headersFor(ticketId) }\n );\n return fromSupportAnswer(data.data, nameFor(ticketId));\n };\n\n return {\n async startConversation({\n message,\n institutionId,\n requesterName,\n email,\n files = [],\n }) {\n // The ticket needs a description; an image-only start uses the names\n const description = message || files.map((file) => file.name).join(', ');\n const { data } = await apiClient.post<{\n data: { id: string; guestToken: string };\n }>('/support/external', {\n description,\n ...(institutionId ? { institutionId } : {}),\n ...(requesterName ? { requesterName } : {}),\n ...(email ? { email } : {}),\n });\n const ticketId = data.data.id;\n sessions.set(ticketId, {\n token: data.data.guestToken,\n name: requesterName || 'Visitante',\n });\n\n // The first message is saved with the ticket; read it back for its id.\n // /support/external takes no files, so they go as the next message.\n let messages = await listMessages(ticketId);\n if (!message && messages[0]) {\n hiddenIds.add(messages[0].id);\n messages = messages.slice(1);\n }\n if (files.length) messages.push(await sendMessage(ticketId, '', files));\n return {\n ticket: {\n id: ticketId,\n status: SUPPORT_CHAT_STATUS.ABERTO,\n handlerType: null,\n problemType: null,\n },\n messages,\n };\n },\n\n sendMessage,\n\n async getArticle(institutionId, articleId) {\n const { data } = await apiClient.get<{\n data: { article: SupportChatArticle };\n }>(`/help-center/${institutionId}/articles/${articleId}`);\n const { id, title, body, updatedAt } = data.data.article;\n return { id, title, body, updatedAt: updatedAt ?? null };\n },\n\n async updateContact(ticketId, contact) {\n if (!apiClient.put) {\n throw new Error('O cliente da API não envia PUT');\n }\n await apiClient.put(`/support/guest/${ticketId}`, contact, {\n headers: headersFor(ticketId),\n });\n const session = sessions.get(ticketId);\n if (session && contact.requesterName) {\n session.name = contact.requesterName;\n }\n },\n\n listMessages,\n\n subscribe(ticketId, listener) {\n // id → what the chat shows besides the text; a message seen again with\n // more of it (a stale WebSocket history may lack contactRequest/faq) is\n // delivered again so the chat replaces it\n const seen = new Map<string, string>();\n const extrasOf = (message: SupportChatMessage) =>\n JSON.stringify([message.contactRequest ?? null, message.faq ?? null]);\n const NO_EXTRAS = JSON.stringify([null, null]);\n let socket: WebSocket | null = null;\n let pollTimer: ReturnType<typeof setInterval> | null = null;\n let active = true;\n\n const deliver = (message: SupportChatMessage) => {\n if (!isVisible(message)) return;\n const extras = extrasOf(message);\n const previous = seen.get(message.id);\n // Same as before, or a poorer copy: nothing new to show\n if (previous === extras || (previous && extras === NO_EXTRAS)) return;\n seen.set(message.id, extras);\n listener({ type: 'new_message', payload: { message } });\n };\n\n const poll = async () => {\n try {\n (await listMessages(ticketId)).forEach(deliver);\n } catch {\n // Next tick tries again\n }\n };\n const startPolling = () => {\n if (pollTimer || !active) return;\n void poll();\n pollTimer = setInterval(poll, pollIntervalMs);\n };\n const stopPolling = () => {\n if (pollTimer) clearInterval(pollTimer);\n pollTimer = null;\n };\n\n const handleFrame = (raw: string) => {\n let event: { type?: string; payload?: Record<string, unknown> };\n try {\n event = JSON.parse(raw);\n } catch {\n return;\n }\n const payload = event.payload ?? {};\n if (event.type === 'history') {\n (payload.messages as SupportChatMessage[]).forEach((message) =>\n deliver({ ...message, attachments: message.attachments ?? [] })\n );\n } else if (event.type === 'new_message') {\n const message = payload.message as SupportChatMessage;\n deliver({ ...message, attachments: message.attachments ?? [] });\n } else if (\n event.type === 'message_updated' ||\n event.type === 'ticket_updated'\n ) {\n listener(event as SupportChatEvent);\n } else if (event.type === 'error') {\n listener({\n type: 'error',\n payload: {\n message: String(payload.message_text ?? payload.message ?? ''),\n },\n });\n }\n };\n\n const token = sessions.get(ticketId)?.token;\n if (wsUrl && token) {\n const url = new URL(wsUrl);\n url.searchParams.set('channel', 'support');\n url.searchParams.set('roomId', ticketId);\n url.searchParams.set('token', token);\n socket = new WebSocket(url.toString());\n socket.onopen = () => {\n stopPolling();\n socket?.send(JSON.stringify({ type: 'getInitialData' }));\n // The REST API is the source of truth: a WebSocket deployed apart\n // from it may send a history without contactRequest/faq\n void poll();\n };\n socket.onmessage = (event) => handleFrame(String(event.data));\n socket.onclose = startPolling;\n socket.onerror = startPolling;\n } else {\n startPolling();\n }\n\n return () => {\n active = false;\n stopPolling();\n if (socket) {\n socket.onclose = null;\n socket.close();\n }\n };\n },\n };\n};\n","import {\n SUPPORT_CHAT_AUTHOR,\n SUPPORT_CHAT_GUEST_SENDER_ID,\n SUPPORT_CHAT_HANDLER,\n SUPPORT_CHAT_STATUS,\n type SupportChatAdapter,\n type SupportChatEvent,\n type SupportChatMessage,\n type SupportChatTicket,\n} from '../../types/supportChat';\n\nexport interface MockSupportChatAdapterOptions {\n /** Latency of every call, in ms (default 300) */\n delayMs?: number;\n /** Delay of the automatic agent reply, in ms (default 1500) */\n replyDelayMs?: number;\n /** Text of the agent reply; null disables it */\n replyText?: string | null;\n}\n\nconst DEFAULT_REPLY =\n 'Olá! Recebemos sua mensagem e já vamos te ajudar. Este é um atendimento de demonstração.';\n\n/**\n * In-memory adapter for the support chat widget. It mirrors the backend\n * contract (statuses, authors, events) so the widget can be built and tested\n * before the real API + WebSocket adapter exists.\n */\nexport const createMockSupportChatAdapter = ({\n delayMs = 300,\n replyDelayMs = 1500,\n replyText = DEFAULT_REPLY,\n}: MockSupportChatAdapterOptions = {}): SupportChatAdapter => {\n const tickets = new Map<string, SupportChatTicket>();\n const messages = new Map<string, SupportChatMessage[]>();\n const requesters = new Map<string, { name: string; logged: boolean }>();\n const listeners = new Map<string, Set<(event: SupportChatEvent) => void>>();\n let sequence = 0;\n\n const wait = () => new Promise((resolve) => setTimeout(resolve, delayMs));\n const nextId = (prefix: string) => `${prefix}-${++sequence}`;\n\n const emit = (ticketId: string, event: SupportChatEvent) => {\n listeners.get(ticketId)?.forEach((listener) => listener(event));\n };\n\n const append = (ticketId: string, message: SupportChatMessage) => {\n messages.set(ticketId, [...(messages.get(ticketId) ?? []), message]);\n };\n\n const toAttachments = (files: File[]) =>\n files.map((file) => ({\n id: nextId('file'),\n url: URL.createObjectURL(file),\n fileName: file.name,\n mimeType: file.type,\n size: file.size,\n }));\n\n const buildRequesterMessage = (\n ticketId: string,\n content: string,\n files: File[] = []\n ): SupportChatMessage => {\n const requester = requesters.get(ticketId);\n return {\n id: nextId('msg'),\n senderId: requester?.logged ? 'user' : SUPPORT_CHAT_GUEST_SENDER_ID,\n senderName: requester?.name ?? 'Visitante',\n senderPhoto: null,\n senderRole: requester?.logged\n ? SUPPORT_CHAT_AUTHOR.USER\n : SUPPORT_CHAT_AUTHOR.GUEST,\n content,\n messageType: 'text',\n createdAt: new Date().toISOString(),\n attachments: toAttachments(files),\n };\n };\n\n const scheduleReply = (ticketId: string) => {\n if (replyText === null) return;\n setTimeout(() => {\n const ticket = tickets.get(ticketId);\n if (!ticket) return;\n\n const reply: SupportChatMessage = {\n id: nextId('msg'),\n senderId: 'agent',\n senderName: 'Suporte',\n senderPhoto: null,\n senderRole: SUPPORT_CHAT_AUTHOR.AGENT,\n content: replyText,\n messageType: 'text',\n createdAt: new Date().toISOString(),\n attachments: [],\n };\n append(ticketId, reply);\n\n const updated: SupportChatTicket = {\n ...ticket,\n status: SUPPORT_CHAT_STATUS.EM_ANDAMENTO,\n handlerType: SUPPORT_CHAT_HANDLER.HUMANO,\n };\n tickets.set(ticketId, updated);\n\n emit(ticketId, { type: 'new_message', payload: { message: reply } });\n emit(ticketId, {\n type: 'ticket_updated',\n payload: {\n ticketId,\n status: updated.status,\n handlerType: updated.handlerType,\n problemType: updated.problemType,\n },\n });\n }, replyDelayMs);\n };\n\n return {\n async getArticle(_institutionId, articleId) {\n await wait();\n return {\n id: articleId,\n title: 'Como gerar e localizar simulados',\n body:\n '<p>Monte um simulado com as matérias que quiser e acompanhe o resultado. 📝</p>' +\n '<h2>Passo a passo</h2>' +\n '<ol><li><p>No menu, abra <strong>Simulados</strong>.</p></li>' +\n '<li><p>Clique em <strong>Gerar simulado</strong> e escolha as áreas.</p></li>' +\n '<li><p>Responda e veja o resultado em <strong>Histórico</strong>.</p></li></ol>' +\n '<p><a href=\"https://www.youtube.com/watch?v=dQw4w9WgXcQ\">Assista ao vídeo do passo a passo</a></p>' +\n '<blockquote><p>💡 Dica: refaça as questões que errou.</p></blockquote>',\n updatedAt: '2026-10-21T12:00:00.000Z',\n };\n },\n\n async startConversation({ message, requesterName, email, files }) {\n await wait();\n const ticket: SupportChatTicket = {\n id: nextId('ticket'),\n status: SUPPORT_CHAT_STATUS.ABERTO,\n handlerType: null,\n problemType: null,\n };\n tickets.set(ticket.id, ticket);\n requesters.set(ticket.id, {\n name: requesterName || 'Visitante',\n logged: Boolean(email),\n });\n\n const first = buildRequesterMessage(ticket.id, message, files);\n append(ticket.id, first);\n scheduleReply(ticket.id);\n return { ticket, messages: [first] };\n },\n\n async sendMessage(ticketId, content, files) {\n await wait();\n const ticket = tickets.get(ticketId);\n if (!ticket) throw new Error('Atendimento não encontrado');\n if (ticket.status === SUPPORT_CHAT_STATUS.FECHADO) {\n throw new Error('Este atendimento já foi encerrado');\n }\n\n const message = buildRequesterMessage(ticketId, content, files);\n append(ticketId, message);\n scheduleReply(ticketId);\n return message;\n },\n\n async listMessages(ticketId) {\n await wait();\n return [...(messages.get(ticketId) ?? [])];\n },\n\n subscribe(ticketId, listener) {\n const set = listeners.get(ticketId) ?? new Set();\n set.add(listener);\n listeners.set(ticketId, set);\n return () => {\n set.delete(listener);\n };\n },\n };\n};\n"],"mappings":"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","names":["useCallback","useEffect","useRef","useState","XIcon","useState","useState","useEffect","useRef","useState","jsx","jsxs","useState","useRef","useEffect","title","useEffect","useState","jsx","jsxs","useState","useEffect","jsx","jsxs","useState","useRef","useCallback","useEffect","XIcon","useMemo","jsx","useMemo"]}
|
|
1
|
+
{"version":3,"sources":["../src/components/SupportChatWidget/SupportChatWidget.tsx","../src/hooks/useSupportChat.ts","../src/components/SupportChatWidget/SupportChatContactForm.tsx","../src/components/SupportChatWidget/SupportChatArticleView.tsx","../src/components/SupportChatWidget/supportChatContent.tsx","../src/components/SupportChatWidget/NativeSupportChat.tsx","../src/components/SupportChatWidget/guestSupportChatAdapter.ts","../src/components/SupportChatWidget/mockSupportChatAdapter.ts"],"sourcesContent":["import {\n useCallback,\n useEffect,\n useRef,\n useState,\n type ChangeEvent,\n type KeyboardEvent,\n} from 'react';\nimport {\n ChatCircleDotsIcon,\n MinusIcon,\n PaperclipIcon,\n PaperPlaneTiltIcon,\n XIcon,\n} from '@phosphor-icons/react';\nimport Text from '../Text/Text';\nimport Button from '../Button/Button';\nimport { useSupportChat } from '../../hooks/useSupportChat';\nimport { SupportChatContactForm } from './SupportChatContactForm';\nimport { SupportChatArticleView } from './SupportChatArticleView';\nimport {\n AttachmentList,\n MessageText,\n PendingAttachment,\n SUPPORT_CHAT_ACCEPT,\n pickAttachments,\n} from './supportChatContent';\nimport { SupportType } from '../../types/support';\nimport {\n SUPPORT_CHAT_AUTHOR,\n SUPPORT_CHAT_MAX_ATTACHMENTS,\n type SupportChatAdapter,\n type SupportChatFlags,\n type SupportChatMessage,\n type SupportChatTheme,\n type SupportChatUser,\n} from '../../types/supportChat';\nimport { cn } from '../../utils/utils';\n\n/** Visibility of the chat window */\nexport type SupportChatWindowState = 'closed' | 'open' | 'minimized';\n\nexport interface SupportChatWidgetProps {\n /** Data layer (createMockSupportChatAdapter for now) */\n adapter: SupportChatAdapter;\n /** App the widget runs in, sent with the ticket (e.g. \"login\", \"backoffice\") */\n channel: string;\n institutionId?: string;\n /** Logged user; omit for visitors */\n user?: SupportChatUser;\n flags?: SupportChatFlags;\n theme?: SupportChatTheme;\n onOpen?: () => void;\n onSend?: (message: SupportChatMessage) => void;\n /**\n * Called when the window is closed with X. Closing only hides it: the\n * conversation stays connected and new replies count as unread.\n */\n onClose?: () => void;\n /**\n * Link of a FAQ article, e.g. `(id) => '/artigo/' + id`. With it, the\n * \"Baseado na FAQ\" line under an AI answer opens the article; without it\n * the line only names the article.\n */\n faqArticleUrl?: (articleId: string) => string;\n}\n\n/** Label read by screen readers on the unread badge of the launcher */\nconst formatNewMessagesLabel = (count: number): string =>\n count === 1 ? '1 nova mensagem' : `${count} novas mensagens`;\n\n/** The requester side of the conversation (user or visitor) */\nconst isOwnMessage = (message: SupportChatMessage) =>\n message.senderRole === SUPPORT_CHAT_AUTHOR.USER ||\n message.senderRole === SUPPORT_CHAT_AUTHOR.GUEST;\n\nconst formatTime = (createdAt: string) =>\n new Date(createdAt).toLocaleTimeString('pt-BR', {\n hour: '2-digit',\n minute: '2-digit',\n });\n\n/** \"Baseado na FAQ: <title>\" under an AI answer, linked when possible */\nconst FaqReference = ({\n faq,\n faqArticleUrl,\n onOpenArticle,\n}: {\n faq: { id: string; title: string };\n faqArticleUrl?: (articleId: string) => string;\n onOpenArticle?: (article: { id: string; title: string }) => void;\n}) => {\n const linked = !!onOpenArticle || !!faqArticleUrl;\n const label = (\n <Text\n as=\"span\"\n size=\"xs\"\n color=\"text-primary-700\"\n className={cn(linked && 'underline')}\n >\n Baseado na FAQ: {faq.title}\n </Text>\n );\n let content = label;\n if (onOpenArticle) {\n // Opens inside the chat, like the Zendesk messenger (no new tab)\n content = (\n <a\n href={faqArticleUrl?.(faq.id) ?? '#'}\n onClick={(event) => {\n event.preventDefault();\n onOpenArticle(faq);\n }}\n >\n {label}\n </a>\n );\n } else if (faqArticleUrl) {\n content = (\n <a href={faqArticleUrl(faq.id)} target=\"_blank\" rel=\"noreferrer\">\n {label}\n </a>\n );\n }\n return (\n <div data-testid=\"support-chat-faq\" className=\"mt-1 max-w-[80%]\">\n {content}\n </div>\n );\n};\n\nconst MessageBubble = ({\n message,\n faqArticleUrl,\n onOpenArticle,\n}: {\n message: SupportChatMessage;\n faqArticleUrl?: (articleId: string) => string;\n onOpenArticle?: (article: { id: string; title?: string }) => void;\n}) => {\n const own = isOwnMessage(message);\n return (\n <div\n className={cn(\n 'flex flex-col mb-3 transition-opacity',\n own ? 'items-end' : 'items-start',\n message.pending && 'opacity-60'\n )}\n aria-busy={message.pending || undefined}\n >\n {!own && (\n <Text size=\"xs\" className=\"text-text-500 mb-1\">\n {message.senderName}\n </Text>\n )}\n <div\n className={cn(\n 'max-w-[80%] rounded-2xl whitespace-pre-wrap break-words',\n // Image-only messages show the image without the colored bubble\n message.content\n ? cn(\n 'px-4 py-2',\n own\n ? 'bg-primary-500 text-white rounded-br-md'\n : 'bg-background-100 text-text-900 rounded-bl-md'\n )\n : 'text-text-900'\n )}\n >\n {message.content && (\n <MessageText\n text={message.content}\n onOpenArticle={\n onOpenArticle ? (id) => onOpenArticle({ id }) : undefined\n }\n />\n )}\n {message.attachments.length > 0 && (\n <AttachmentList items={message.attachments} />\n )}\n </div>\n {message.faq && (\n <FaqReference\n faq={message.faq}\n faqArticleUrl={faqArticleUrl}\n onOpenArticle={onOpenArticle}\n />\n )}\n <Text size=\"xs\" className=\"text-text-400 mt-1\">\n {message.pending ? 'Enviando…' : formatTime(message.createdAt)}\n </Text>\n </div>\n );\n};\n\n/**\n * Floating support chat (native support, the Zendesk replacement).\n * Renders nothing when disabled or when the institution uses Zendesk.\n */\nexport const SupportChatWidget = ({\n flags,\n ...props\n}: SupportChatWidgetProps) => {\n const hidden =\n flags?.enabled === false ||\n flags?.chat === false ||\n flags?.supportType === SupportType.ZENDESK;\n\n if (hidden) return null;\n return <SupportChatWidgetContent {...props} />;\n};\n\nconst SupportChatWidgetContent = ({\n adapter,\n channel,\n institutionId,\n user,\n theme,\n onOpen,\n onSend,\n onClose,\n faqArticleUrl,\n}: Omit<SupportChatWidgetProps, 'flags'>) => {\n const [windowState, setWindowState] =\n useState<SupportChatWindowState>('closed');\n const [draft, setDraft] = useState('');\n const [files, setFiles] = useState<File[]>([]);\n const [fileError, setFileError] = useState<string | null>(null);\n const [unread, setUnread] = useState(0);\n // FAQ article open inside the chat (the conversation stays as it was)\n const [article, setArticle] = useState<{ id: string; title?: string } | null>(\n null\n );\n const [expanded, setExpanded] = useState(false);\n const listRef = useRef<HTMLDivElement>(null);\n const fileInputRef = useRef<HTMLInputElement>(null);\n const seenCount = useRef(0);\n\n const { messages, sending, error, closed, send, updateContact, reset } =\n useSupportChat({\n adapter,\n channel,\n institutionId,\n user,\n });\n const [savedContactRequests, setSavedContactRequests] = useState<string[]>(\n []\n );\n\n // Latest AI request for name/e-mail: the form goes under it, and the\n // composer waits until it is sent\n const contactRequest = updateContact\n ? [...messages].reverse().find((message) => message.contactRequest?.length)\n : undefined;\n const awaitingContact =\n !!contactRequest && !savedContactRequests.includes(contactRequest.id);\n\n const isOpen = windowState === 'open';\n\n const canOpenArticle = !!adapter.getArticle && !!institutionId;\n const loadArticle = useCallback(\n (articleId: string) => {\n if (!adapter.getArticle || !institutionId) {\n return Promise.reject(new Error('Artigo indisponível'));\n }\n // Called on the adapter so a class-based adapter keeps its `this`\n return adapter.getArticle(institutionId, articleId);\n },\n [adapter, institutionId]\n );\n const openArticle = canOpenArticle\n ? (target: { id: string; title?: string }) => {\n setArticle(target);\n setExpanded(true);\n }\n : undefined;\n const closeArticle = () => {\n setArticle(null);\n setExpanded(false);\n };\n const title = theme?.title ?? 'Suporte';\n const left = theme?.position === 'bottom-left';\n const accentStyle = theme?.primaryColor\n ? { backgroundColor: theme.primaryColor }\n : undefined;\n\n // Count the support replies that arrive while the window is not visible\n useEffect(() => {\n const fresh = messages.slice(seenCount.current);\n seenCount.current = messages.length;\n if (isOpen) return;\n const replies = fresh.filter((message) => !isOwnMessage(message)).length;\n if (replies) setUnread((count) => count + replies);\n }, [messages, isOpen]);\n\n useEffect(() => {\n if (!isOpen || article) return;\n setUnread(0);\n listRef.current?.scrollTo?.({ top: listRef.current.scrollHeight });\n }, [isOpen, messages, article]);\n\n const open = () => {\n setWindowState('open');\n onOpen?.();\n };\n\n const close = () => {\n setWindowState('closed');\n onClose?.();\n };\n\n /** Drop a finished conversation so the next message opens a new ticket */\n const startOver = () => {\n setDraft('');\n setFiles([]);\n setFileError(null);\n setUnread(0);\n seenCount.current = 0;\n setSavedContactRequests([]);\n reset();\n };\n\n const submit = async () => {\n const content = draft;\n const pending = files;\n if (!content.trim() && pending.length === 0) return;\n setDraft('');\n setFiles([]);\n setFileError(null);\n const message = await send(content, pending);\n if (message) {\n onSend?.(message);\n } else {\n setDraft(content);\n setFiles(pending);\n }\n };\n\n const handleFiles = (event: ChangeEvent<HTMLInputElement>) => {\n const { accepted, problem } = pickAttachments(\n files,\n Array.from(event.target.files ?? [])\n );\n setFiles(accepted);\n setFileError(problem);\n event.target.value = '';\n };\n\n const handleKeyDown = (event: KeyboardEvent<HTMLTextAreaElement>) => {\n if (event.key === 'Enter' && !event.shiftKey) {\n event.preventDefault();\n void submit();\n }\n };\n\n return (\n <div\n className={cn(\n 'fixed bottom-4 z-50 flex flex-col gap-3',\n left ? 'left-4 items-start' : 'right-4 items-end'\n )}\n >\n {isOpen && (\n <section\n role=\"dialog\"\n aria-label={title}\n className={cn(\n 'max-w-[calc(100vw-2rem)] max-h-[calc(100vh-6rem)] flex flex-col bg-background rounded-2xl shadow-lg border border-border-100 overflow-hidden transition-[width,height]',\n expanded ? 'w-[720px] h-[615px]' : 'w-[360px] h-[520px]'\n )}\n >\n {article ? (\n <SupportChatArticleView\n articleId={article.id}\n initialTitle={article.title}\n loadArticle={loadArticle}\n expanded={expanded}\n onToggleExpanded={() => setExpanded((value) => !value)}\n onBack={closeArticle}\n externalUrl={faqArticleUrl?.(article.id)}\n accentStyle={accentStyle}\n />\n ) : (\n <>\n <header\n className=\"flex items-center justify-between px-4 py-3 bg-primary-500 text-white\"\n style={accentStyle}\n >\n <Text size=\"md\" weight=\"semibold\" color=\"\">\n {title}\n </Text>\n <div className=\"flex items-center gap-1\">\n <Button\n variant=\"raw\"\n aria-label=\"Minimizar\"\n onClick={() => setWindowState('minimized')}\n className=\"p-1 rounded hover:bg-white/20\"\n >\n <MinusIcon size={18} />\n </Button>\n <Button\n variant=\"raw\"\n aria-label=\"Fechar\"\n onClick={close}\n className=\"p-1 rounded hover:bg-white/20\"\n >\n <XIcon size={18} />\n </Button>\n </div>\n </header>\n\n <div ref={listRef} className=\"flex-1 overflow-y-auto px-4 py-3\">\n {messages.length === 0 ? (\n <Text size=\"sm\" className=\"text-text-500 text-center mt-8\">\n Olá! Como podemos te ajudar?\n </Text>\n ) : (\n messages.map((message) => (\n <div key={message.id}>\n <MessageBubble\n message={message}\n faqArticleUrl={faqArticleUrl}\n onOpenArticle={openArticle}\n />\n {updateContact &&\n message.id === contactRequest?.id &&\n message.contactRequest && (\n <SupportChatContactForm\n fields={message.contactRequest}\n accentColor={theme?.primaryColor}\n onSubmit={async (contact) => {\n const ok = await updateContact(contact);\n if (ok) {\n setSavedContactRequests((ids) => [\n ...ids,\n message.id,\n ]);\n }\n return ok;\n }}\n />\n )}\n </div>\n ))\n )}\n </div>\n\n {(fileError || error) && (\n <Text\n size=\"xs\"\n role=\"alert\"\n className=\"px-4 py-2 text-error-700 bg-error-50\"\n >\n {fileError ?? error}\n </Text>\n )}\n\n {closed ? (\n <div className=\"flex flex-col items-center gap-2 px-4 py-3 border-t border-border-100\">\n <Text size=\"sm\" color=\"text-text-500\" className=\"text-center\">\n Este atendimento foi encerrado.\n </Text>\n <Button variant=\"link\" size=\"small\" onClick={startOver}>\n Iniciar nova conversa\n </Button>\n </div>\n ) : (\n <form\n className=\"flex flex-col gap-2 px-3 py-3 border-t border-border-100\"\n onSubmit={(event) => {\n event.preventDefault();\n void submit();\n }}\n >\n {files.length > 0 && (\n <div className=\"flex flex-wrap gap-2\">\n {files.map((file, index) => (\n <PendingAttachment\n key={`${file.name}-${index}`}\n file={file}\n onRemove={() =>\n setFiles((list) =>\n list.filter((_, i) => i !== index)\n )\n }\n />\n ))}\n </div>\n )}\n <div className=\"flex items-end gap-2\">\n <input\n ref={fileInputRef}\n type=\"file\"\n accept={SUPPORT_CHAT_ACCEPT}\n multiple\n hidden\n data-testid=\"support-chat-file-input\"\n onChange={handleFiles}\n />\n <Button\n variant=\"raw\"\n aria-label=\"Anexar arquivo\"\n disabled={\n sending ||\n awaitingContact ||\n files.length >= SUPPORT_CHAT_MAX_ATTACHMENTS\n }\n onClick={() => fileInputRef.current?.click()}\n className=\"p-2 rounded-full text-text-600 hover:bg-background-100 disabled:opacity-50\"\n >\n <PaperclipIcon size={20} />\n </Button>\n <textarea\n aria-label=\"Mensagem\"\n placeholder={\n awaitingContact\n ? 'Preencha seus dados acima'\n : 'Digite sua mensagem'\n }\n disabled={awaitingContact}\n rows={1}\n value={draft}\n onChange={(event) => setDraft(event.target.value)}\n onKeyDown={handleKeyDown}\n className=\"flex-1 resize-none rounded-xl border border-border-300 px-3 py-2 text-sm text-text-900 focus:outline-none focus:border-primary-500 max-h-28\"\n />\n <Button\n variant=\"raw\"\n type=\"submit\"\n aria-label=\"Enviar\"\n disabled={\n sending ||\n awaitingContact ||\n (!draft.trim() && files.length === 0)\n }\n className=\"p-2 rounded-full bg-primary-500 text-white disabled:opacity-50\"\n style={accentStyle}\n >\n <PaperPlaneTiltIcon size={18} />\n </Button>\n </div>\n </form>\n )}\n </>\n )}\n </section>\n )}\n\n {!isOpen && (\n <Button\n variant=\"raw\"\n aria-label={`Abrir ${title.toLowerCase()}`}\n onClick={open}\n className=\"relative w-14 h-14 rounded-full bg-primary-500 text-white shadow-lg flex items-center justify-center hover:brightness-110\"\n style={accentStyle}\n >\n <ChatCircleDotsIcon size={28} />\n {unread > 0 && (\n <Text\n as=\"span\"\n size=\"xs\"\n color=\"text-white\"\n aria-label={formatNewMessagesLabel(unread)}\n className=\"absolute -top-1 -right-1 min-w-5 h-5 px-1 rounded-full bg-error-500 flex items-center justify-center\"\n >\n {unread}\n </Text>\n )}\n </Button>\n )}\n </div>\n );\n};\n","import { useCallback, useEffect, useRef, useState } from 'react';\nimport {\n SUPPORT_CHAT_AUTHOR,\n SUPPORT_CHAT_GUEST_SENDER_ID,\n SUPPORT_CHAT_STATUS,\n type SupportChatAdapter,\n type SupportChatContact,\n type SupportChatEvent,\n type SupportChatMessage,\n type SupportChatTicket,\n type SupportChatUser,\n} from '../types/supportChat';\n\nexport interface UseSupportChatConfig {\n adapter: SupportChatAdapter;\n channel: string;\n institutionId?: string;\n user?: SupportChatUser;\n}\n\nexport interface UseSupportChatReturn {\n ticket: SupportChatTicket | null;\n messages: SupportChatMessage[];\n sending: boolean;\n error: string | null;\n /** Ticket resolved: no more messages can be sent */\n closed: boolean;\n /** Send a message (text and/or files); the first one opens the ticket */\n send: (content: string, files?: File[]) => Promise<SupportChatMessage | null>;\n /**\n * Save the contact typed in the form shown under the AI request. Resolves\n * false (and sets `error`) when it fails. Undefined when the adapter cannot\n * save it.\n */\n updateContact?: (contact: SupportChatContact) => Promise<boolean>;\n /** Forget the current conversation */\n reset: () => void;\n}\n\nlet pendingSequence = 0;\n\n/**\n * Message shown right away while it is being sent (optimistic rendering).\n * Images use local object URLs, released when the real message arrives.\n */\nconst buildPendingMessage = (\n content: string,\n files: File[],\n user?: SupportChatUser\n): SupportChatMessage => {\n pendingSequence += 1;\n return {\n id: `pending-${pendingSequence}`,\n senderId: SUPPORT_CHAT_GUEST_SENDER_ID,\n senderName: user?.name || 'Você',\n senderPhoto: null,\n senderRole: user?.email\n ? SUPPORT_CHAT_AUTHOR.USER\n : SUPPORT_CHAT_AUTHOR.GUEST,\n content,\n messageType: 'text',\n createdAt: new Date().toISOString(),\n attachments: files.map((file, index) => ({\n id: `pending-${pendingSequence}-${index}`,\n url: URL.createObjectURL(file),\n fileName: file.name,\n mimeType: file.type,\n size: file.size,\n })),\n pending: true,\n };\n};\n\nconst releasePending = (message: SupportChatMessage) =>\n message.attachments.forEach((item) => URL.revokeObjectURL(item.url));\n\nconst getErrorMessage = (error: unknown) =>\n error instanceof Error ? error.message : 'Não foi possível enviar a mensagem';\n\n/** Add or replace a message, keeping the list ordered by arrival */\nconst upsertMessage = (\n list: SupportChatMessage[],\n message: SupportChatMessage\n): SupportChatMessage[] =>\n list.some((item) => item.id === message.id)\n ? list.map((item) => (item.id === message.id ? message : item))\n : [...list, message];\n\n/**\n * State of a support conversation on top of a SupportChatAdapter.\n * The REST-like calls write; the subscription only applies the events,\n * so a message received both ways is stored once.\n */\nexport const useSupportChat = ({\n adapter,\n channel,\n institutionId,\n user,\n}: UseSupportChatConfig): UseSupportChatReturn => {\n const [ticket, setTicket] = useState<SupportChatTicket | null>(null);\n const [messages, setMessages] = useState<SupportChatMessage[]>([]);\n const [sending, setSending] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const sendingRef = useRef(false);\n\n const ticketId = ticket?.id;\n\n useEffect(() => {\n if (!ticketId) return;\n\n const handleEvent = (event: SupportChatEvent) => {\n switch (event.type) {\n case 'new_message':\n setMessages((list) => upsertMessage(list, event.payload.message));\n break;\n case 'message_updated':\n setMessages((list) =>\n list.map((item) =>\n item.id === event.payload.messageId\n ? { ...item, content: event.payload.content }\n : item\n )\n );\n break;\n case 'ticket_updated': {\n const { ticketId: id, ...rest } = event.payload;\n setTicket((current) =>\n current?.id === id ? { ...current, ...rest } : current\n );\n break;\n }\n case 'error':\n setError(event.payload.message);\n break;\n }\n };\n\n return adapter.subscribe(ticketId, handleEvent);\n }, [adapter, ticketId]);\n\n const send = useCallback(\n async (content: string, files: File[] = []) => {\n const text = content.trim();\n if ((!text && files.length === 0) || sendingRef.current) return null;\n\n sendingRef.current = true;\n setSending(true);\n setError(null);\n\n const pending = buildPendingMessage(text, files, user);\n setMessages((list) => [...list, pending]);\n // Swap the optimistic message for the confirmed ones\n const confirm = (confirmed: SupportChatMessage[]) =>\n setMessages((list) =>\n confirmed.reduce(\n upsertMessage,\n list.filter((item) => item.id !== pending.id)\n )\n );\n\n try {\n if (ticketId) {\n const message = await adapter.sendMessage(ticketId, text, files);\n confirm([message]);\n return message;\n }\n\n const started = await adapter.startConversation({\n message: text,\n channel,\n institutionId,\n requesterName: user?.name,\n email: user?.email,\n files,\n });\n setTicket(started.ticket);\n confirm(started.messages);\n return started.messages[started.messages.length - 1] ?? null;\n } catch (sendError) {\n setMessages((list) => list.filter((item) => item.id !== pending.id));\n setError(getErrorMessage(sendError));\n return null;\n } finally {\n releasePending(pending);\n sendingRef.current = false;\n setSending(false);\n }\n },\n [adapter, channel, institutionId, ticketId, user]\n );\n\n const updateContact = useCallback(\n async (contact: SupportChatContact) => {\n if (!ticketId || !adapter.updateContact) return false;\n setError(null);\n try {\n await adapter.updateContact(ticketId, contact);\n return true;\n } catch (contactError) {\n setError(\n (contactError as { response?: { status?: number } } | null)?.response\n ?.status === 409\n ? 'O e-mail deste atendimento já foi informado'\n : 'Não foi possível salvar seus dados. Tente de novo.'\n );\n return false;\n }\n },\n [adapter, ticketId]\n );\n\n const reset = useCallback(() => {\n setTicket(null);\n setMessages([]);\n setError(null);\n }, []);\n\n return {\n ticket,\n messages,\n sending,\n error,\n closed: ticket?.status === SUPPORT_CHAT_STATUS.FECHADO,\n send,\n updateContact: adapter.updateContact ? updateContact : undefined,\n reset,\n };\n};\n","import { useState, type FormEvent } from 'react';\nimport Text from '../Text/Text';\nimport Button from '../Button/Button';\nimport Input from '../Input/Input';\nimport type {\n SupportChatContact,\n SupportChatContactField,\n} from '../../types/supportChat';\n\nexport interface SupportChatContactFormProps {\n /** Fields the AI asked for, in order (one step each) */\n fields: SupportChatContactField[];\n /** Saves the contact; resolves true when it worked */\n onSubmit: (contact: SupportChatContact) => Promise<boolean>;\n /** Accent color of the theme, for the button */\n accentColor?: string;\n}\n\nconst STEPS: Record<\n SupportChatContactField,\n { label: string; type: string; autoComplete: string; placeholder: string }\n> = {\n name: {\n label: 'Seu nome',\n type: 'text',\n autoComplete: 'name',\n placeholder: 'Como podemos te chamar?',\n },\n email: {\n label: 'Seu e-mail',\n type: 'email',\n autoComplete: 'email',\n placeholder: 'nome@exemplo.com',\n },\n};\n\nconst EMAIL_PATTERN = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/;\n\n/** Problem with a typed value, or null when it can be sent */\nconst validate = (\n field: SupportChatContactField,\n value: string\n): string | null => {\n if (field === 'name') {\n return value.length >= 2 ? null : 'Digite seu nome';\n }\n return EMAIL_PATTERN.test(value) ? null : 'Digite um e-mail válido';\n};\n\n/**\n * Step-by-step form for the contact the AI asks on the first contact (name,\n * then e-mail), shown under the AI message like the Zendesk pre-chat form.\n * The values go to the backend as fields, not as a chat message. Once sent,\n * it shows what was saved.\n */\nexport const SupportChatContactForm = ({\n fields,\n onSubmit,\n accentColor,\n}: SupportChatContactFormProps) => {\n const [step, setStep] = useState(0);\n const [values, setValues] = useState<Record<string, string>>({});\n const [problem, setProblem] = useState<string | null>(null);\n const [saving, setSaving] = useState(false);\n const [saved, setSaved] = useState(false);\n\n const field = fields[step];\n const config = STEPS[field];\n const value = values[field] ?? '';\n const isLast = step === fields.length - 1;\n\n if (saved) {\n return (\n <div\n data-testid=\"support-chat-contact-saved\"\n className=\"mb-3 ml-1 flex max-w-[85%] flex-col gap-1 rounded-2xl border border-border-100 px-4 py-3\"\n >\n {fields.map((item) => (\n <Text key={item} size=\"xs\" color=\"text-text-600\">\n {STEPS[item].label}:{' '}\n <Text as=\"span\" size=\"xs\" weight=\"semibold\" color=\"text-text-900\">\n {values[item]}\n </Text>\n </Text>\n ))}\n </div>\n );\n }\n\n const advance = async (event: FormEvent) => {\n event.preventDefault();\n const typed = field === 'email' ? value.trim().toLowerCase() : value.trim();\n const error = validate(field, typed);\n setProblem(error);\n if (error) return;\n\n const next = { ...values, [field]: typed };\n setValues(next);\n if (!isLast) {\n setStep(step + 1);\n return;\n }\n\n setSaving(true);\n const ok = await onSubmit({\n ...(next.name ? { requesterName: next.name } : {}),\n ...(next.email ? { email: next.email } : {}),\n });\n setSaving(false);\n if (ok) setSaved(true);\n };\n\n const inputId = `support-chat-contact-${field}`;\n\n return (\n <form\n aria-label=\"Seus dados\"\n noValidate\n onSubmit={(event) => void advance(event)}\n className=\"mb-3 ml-1 flex max-w-[85%] flex-col gap-3 rounded-2xl border border-border-100 px-4 py-3\"\n >\n <Input\n id={inputId}\n key={field}\n label={config.label}\n variant=\"rounded\"\n size=\"small\"\n type={config.type}\n autoComplete={config.autoComplete}\n placeholder={config.placeholder}\n value={value}\n autoFocus\n disabled={saving}\n errorMessage={problem ?? undefined}\n onChange={(event) =>\n setValues((current) => ({ ...current, [field]: event.target.value }))\n }\n />\n <div className=\"flex items-center justify-between\">\n <Text size=\"xs\" color=\"text-text-500\">\n {fields.length > 1 ? `${step + 1} de ${fields.length}` : ''}\n </Text>\n <Button\n type=\"submit\"\n size=\"small\"\n disabled={saving}\n style={accentColor ? { backgroundColor: accentColor } : undefined}\n >\n {isLast ? 'Enviar' : 'Avançar'}\n </Button>\n </div>\n </form>\n );\n};\n","import { useEffect, useMemo, useRef, useState } from 'react';\nimport {\n ArrowLeftIcon,\n ArrowSquareOutIcon,\n ArrowsInSimpleIcon,\n ArrowsOutSimpleIcon,\n} from '@phosphor-icons/react';\nimport Text from '../Text/Text';\nimport Button from '../Button/Button';\nimport { ArticleContent } from '../ArticleContent/ArticleContent';\nimport { embedVideoLinks } from '../../utils/videoEmbed';\nimport type { SupportChatArticle } from '../../types/supportChat';\nimport { cn } from '../../utils/utils';\n\nexport interface SupportChatArticleViewProps {\n /** Article to show */\n articleId: string;\n /** Title known from the chat, shown while the article loads */\n initialTitle?: string;\n /** Loads the article (adapter.getArticle bound to the institution) */\n loadArticle: (articleId: string) => Promise<SupportChatArticle>;\n /** Whether the chat window is in the wide mode */\n expanded: boolean;\n onToggleExpanded: () => void;\n /** Back to the conversation (it is kept as it was) */\n onBack: () => void;\n /** Page of the article in the help center, for \"Abrir em nova aba\" */\n externalUrl?: string;\n /** Header background (theme) */\n accentStyle?: { backgroundColor: string };\n}\n\nconst formatUpdatedAt = (iso: string) =>\n new Date(iso).toLocaleDateString('pt-BR', {\n day: '2-digit',\n month: '2-digit',\n year: 'numeric',\n });\n\n/**\n * FAQ article read inside the support chat, like the Zendesk messenger: the\n * conversation is replaced by the article (no new tab), with back,\n * expand/reduce and open-in-new-tab controls. The big title moves to the\n * header once it scrolls out of view. YouTube/Vimeo links of the article\n * become players.\n */\nexport const SupportChatArticleView = ({\n articleId,\n initialTitle,\n loadArticle,\n expanded,\n onToggleExpanded,\n onBack,\n externalUrl,\n accentStyle,\n}: SupportChatArticleViewProps) => {\n const [article, setArticle] = useState<SupportChatArticle | null>(null);\n const [failed, setFailed] = useState(false);\n const [titleHidden, setTitleHidden] = useState(false);\n const titleRef = useRef<HTMLHeadingElement>(null);\n\n useEffect(() => {\n let active = true;\n setArticle(null);\n setFailed(false);\n loadArticle(articleId)\n .then((loaded) => {\n if (active) setArticle(loaded);\n })\n .catch(() => {\n if (active) setFailed(true);\n });\n return () => {\n active = false;\n };\n }, [articleId, loadArticle]);\n\n // The big title leaves the screen → show it in the header\n useEffect(() => {\n const title = titleRef.current;\n if (!title || typeof IntersectionObserver === 'undefined') {\n return undefined;\n }\n const observer = new IntersectionObserver(([entry]) =>\n setTitleHidden(!entry.isIntersecting)\n );\n observer.observe(title);\n return () => observer.disconnect();\n }, [article]);\n\n const html = useMemo(\n () => (article ? embedVideoLinks(article.body) : ''),\n [article]\n );\n const title = article?.title ?? initialTitle ?? 'Artigo';\n\n return (\n <div\n data-testid=\"support-chat-article\"\n className=\"flex min-h-0 flex-1 flex-col\"\n >\n <header\n className=\"flex items-center gap-1 px-2 py-2 bg-primary-500 text-white\"\n style={accentStyle}\n >\n <Button\n variant=\"raw\"\n aria-label=\"Voltar para a janela de mensagens\"\n onClick={onBack}\n className=\"p-1 rounded hover:bg-white/20\"\n >\n <ArrowLeftIcon size={18} />\n </Button>\n <Text\n as=\"span\"\n size=\"sm\"\n weight=\"semibold\"\n color=\"\"\n aria-hidden={!titleHidden || undefined}\n className={cn(\n 'min-w-0 flex-1 truncate transition-opacity',\n titleHidden ? 'opacity-100' : 'opacity-0'\n )}\n >\n {title}\n </Text>\n <Button\n variant=\"raw\"\n aria-label={expanded ? 'Reduzir artigo' : 'Expandir artigo'}\n onClick={onToggleExpanded}\n className=\"p-1 rounded hover:bg-white/20\"\n >\n {expanded ? (\n <ArrowsInSimpleIcon size={18} />\n ) : (\n <ArrowsOutSimpleIcon size={18} />\n )}\n </Button>\n {externalUrl && (\n <a\n href={externalUrl}\n target=\"_blank\"\n rel=\"noreferrer\"\n aria-label=\"Abrir em nova aba\"\n className=\"p-1 rounded hover:bg-white/20\"\n >\n <ArrowSquareOutIcon size={18} />\n </a>\n )}\n </header>\n\n <div className=\"flex-1 overflow-y-auto px-5 py-4\">\n <Text\n as=\"h2\"\n ref={titleRef}\n size=\"xl\"\n weight=\"bold\"\n color=\"text-text-950\"\n >\n {title}\n </Text>\n {article?.updatedAt && (\n <Text size=\"xs\" color=\"text-text-500\" className=\"mt-1\">\n Atualizado em {formatUpdatedAt(article.updatedAt)}\n </Text>\n )}\n\n <div className=\"mt-4\">\n {failed && (\n <Text size=\"sm\" role=\"alert\" color=\"text-error-700\">\n Não foi possível abrir o artigo.\n </Text>\n )}\n {!failed && !article && (\n <Text size=\"sm\" color=\"text-text-500\">\n Carregando…\n </Text>\n )}\n {article && <ArticleContent html={html} />}\n </div>\n </div>\n </div>\n );\n};\n","import { useEffect, useState, type ReactNode } from 'react';\nimport {\n FileIcon,\n FilePdfIcon,\n FileVideoIcon,\n XIcon,\n} from '@phosphor-icons/react';\nimport Text from '../Text/Text';\nimport Button from '../Button/Button';\nimport {\n SUPPORT_CHAT_ATTACHMENT_RULES,\n SUPPORT_CHAT_MAX_ATTACHMENTS,\n type SupportChatAttachment,\n} from '../../types/supportChat';\n\n/** Value of the `accept` attribute of the file picker */\nexport const SUPPORT_CHAT_ACCEPT = Object.keys(\n SUPPORT_CHAT_ATTACHMENT_RULES\n).join(',');\n\nconst isImage = (mimeType?: string) => Boolean(mimeType?.startsWith('image/'));\nconst isVideo = (mimeType?: string) => Boolean(mimeType?.startsWith('video/'));\n\nconst formatSize = (bytes: number) => `${Math.round(bytes / (1024 * 1024))} MB`;\n\n/**\n * Keep the files the backend accepts (type, size and count) and say why the\n * others were left out.\n * @param current - Files already picked\n * @param picked - Files just chosen\n * @returns The accepted files and the last problem found, if any\n */\nexport const pickAttachments = (current: File[], picked: File[]) => {\n const accepted = [...current];\n let problem: string | null = null;\n for (const file of picked) {\n const maxBytes = SUPPORT_CHAT_ATTACHMENT_RULES[file.type];\n if (!maxBytes) {\n problem =\n 'Envie imagens (JPG, PNG, GIF, WEBP), PDF ou vídeo (MP4, WEBM, MOV)';\n } else if (file.size > maxBytes) {\n problem = `${file.name} passa de ${formatSize(maxBytes)}`;\n } else if (accepted.length >= SUPPORT_CHAT_MAX_ATTACHMENTS) {\n problem = `Máximo de ${SUPPORT_CHAT_MAX_ATTACHMENTS} arquivos por mensagem`;\n } else {\n accepted.push(file);\n }\n }\n return { accepted, problem };\n};\n\nconst FileTypeIcon = ({ mimeType }: { mimeType?: string }) => {\n if (mimeType === 'application/pdf') return <FilePdfIcon size={18} />;\n if (isVideo(mimeType)) return <FileVideoIcon size={18} />;\n return <FileIcon size={18} />;\n};\n\n/** Files of a message: images and videos inline, other files as links */\nexport const AttachmentList = ({\n items,\n}: {\n items: SupportChatAttachment[];\n}) => (\n <div className=\"flex flex-wrap gap-2 first:mt-0 mt-1\">\n {items.map((item) => {\n if (isImage(item.mimeType)) {\n return (\n <a key={item.id} href={item.url} target=\"_blank\" rel=\"noreferrer\">\n <img\n src={item.url}\n alt={item.fileName}\n className=\"max-w-[200px] max-h-[200px] rounded-lg object-cover\"\n />\n </a>\n );\n }\n if (isVideo(item.mimeType)) {\n return (\n <video\n key={item.id}\n src={item.url}\n controls\n preload=\"metadata\"\n aria-label={item.fileName}\n className=\"max-w-[240px] max-h-[200px] rounded-lg bg-black\"\n />\n );\n }\n return (\n <a\n key={item.id}\n href={item.url}\n target=\"_blank\"\n rel=\"noreferrer\"\n className=\"flex items-center gap-1 underline\"\n >\n <FileTypeIcon mimeType={item.mimeType} />\n <Text as=\"span\" size=\"sm\" color=\"\">\n {item.fileName}\n </Text>\n </a>\n );\n })}\n </div>\n);\n\n/** A file picked but not sent yet: thumbnail for images, name for the rest */\nexport const PendingAttachment = ({\n file,\n onRemove,\n}: {\n file: File;\n onRemove: () => void;\n}) => {\n const image = isImage(file.type);\n const [url, setUrl] = useState('');\n useEffect(() => {\n if (!image) return undefined;\n const objectUrl = URL.createObjectURL(file);\n setUrl(objectUrl);\n return () => URL.revokeObjectURL(objectUrl);\n }, [file, image]);\n\n return (\n <div className=\"relative\">\n {image ? (\n url && (\n <img\n src={url}\n alt={file.name}\n className=\"w-14 h-14 rounded-lg object-cover border border-border-100\"\n />\n )\n ) : (\n <div className=\"flex h-14 max-w-[160px] items-center gap-1 rounded-lg border border-border-100 px-2 text-text-700\">\n <FileTypeIcon mimeType={file.type} />\n <Text as=\"span\" size=\"xs\" className=\"truncate\">\n {file.name}\n </Text>\n </div>\n )}\n <Button\n variant=\"raw\"\n aria-label={`Remover ${file.name}`}\n onClick={onRemove}\n className=\"absolute -top-1 -right-1 w-5 h-5 rounded-full bg-text-700 text-white flex items-center justify-center\"\n >\n <XIcon size={12} />\n </Button>\n </div>\n );\n};\n\n/** http(s) addresses inside a message */\nconst URL_PATTERN = /https?:\\/\\/[^\\s<>\"]+/g;\n\n/** Punctuation that ends a sentence, not the address */\nconst TRAILING_PUNCTUATION = /[.,;:!?)\\]]+$/;\n\n/** A FAQ article page: `/artigo/<uuid>` */\nconst ARTICLE_PATH =\n /\\/artigo\\/([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})/i;\n\n/**\n * Message text with its addresses as links. A link to a FAQ article opens in\n * the chat (when `onOpenArticle` is given); the others open a new tab.\n */\nexport const MessageText = ({\n text,\n onOpenArticle,\n}: {\n text: string;\n onOpenArticle?: (articleId: string) => void;\n}) => {\n const parts: ReactNode[] = [];\n let cursor = 0;\n for (const match of text.matchAll(URL_PATTERN)) {\n const raw = match[0];\n const url = raw.replace(TRAILING_PUNCTUATION, '');\n const start = match.index ?? 0;\n if (start > cursor) parts.push(text.slice(cursor, start));\n\n const articleId = onOpenArticle ? ARTICLE_PATH.exec(url)?.[1] : undefined;\n parts.push(\n <a\n key={`${start}-${url}`}\n href={url}\n target=\"_blank\"\n rel=\"noreferrer\"\n className=\"underline break-all\"\n onClick={\n articleId\n ? (event) => {\n event.preventDefault();\n onOpenArticle?.(articleId);\n }\n : undefined\n }\n >\n {url}\n </a>\n );\n cursor = start + url.length;\n }\n if (cursor < text.length) parts.push(text.slice(cursor));\n\n return (\n <Text size=\"sm\" color=\"\" className=\"whitespace-pre-wrap\">\n {parts}\n </Text>\n );\n};\n","import { useMemo } from 'react';\nimport { SupportChatWidget } from './SupportChatWidget';\nimport { createGuestSupportChatAdapter } from './guestSupportChatAdapter';\nimport { useSupportFeatureFlag } from '../../hooks/useSupportFeatureFlag';\nimport { useAppStore } from '../../store/appStore';\nimport { useAuthStore } from '../../store/authStore';\nimport { SupportType, type SupportApiClient } from '../../types/support';\nimport type {\n SupportChatHttpClient,\n SupportChatTheme,\n} from '../../types/supportChat';\n\nexport interface NativeSupportChatProps {\n /** Client of the backend API (the app axios instance fits as it is) */\n apiClient: SupportApiClient & SupportChatHttpClient;\n /** App the chat runs in, sent with the ticket (e.g. \"aluno\", \"login\") */\n channel: string;\n /**\n * Extra condition of the app to show the chat, on top of the SUPPORT flag\n * (e.g. logged in and with the support module). Default: true.\n */\n enabled?: boolean;\n /**\n * Link of a FAQ article. Default: the FAQ address of the SUPPORT flag\n * (`<faqUrl>/artigo/<id>`).\n */\n faqArticleUrl?: (articleId: string) => string;\n /** Support WebSocket; without it the chat polls the REST API */\n wsUrl?: string;\n theme?: SupportChatTheme;\n}\n\n/**\n * Native support chat ready to drop in an app: reads the SUPPORT feature flag\n * of the institution (from the app store), the logged user (from the auth\n * store) and talks to the support API by itself.\n *\n * Shown only when the institution uses the native support with the internal\n * support and its chat switched on in the backoffice, once the flag is read,\n * and when `enabled`. With Zendesk it renders nothing (the app loads the\n * Zendesk widget). A logged user opens the ticket with their name and e-mail;\n * a visitor gives them to the AI on the first contact.\n *\n * @example\n * <NativeSupportChat apiClient={api} channel=\"aluno\" enabled={hasSupport} />\n */\nexport const NativeSupportChat = ({\n apiClient,\n channel,\n enabled = true,\n faqArticleUrl,\n wsUrl,\n theme,\n}: NativeSupportChatProps) => {\n const { institutionId } = useAppStore();\n const userName = useAuthStore(\n (state) => state.user?.name ?? state.sessionInfo?.userName\n );\n const userEmail = useAuthStore((state) => state.user?.email);\n const flag = useSupportFeatureFlag({ apiClient });\n const adapter = useMemo(\n () => createGuestSupportChatAdapter({ apiClient, wsUrl }),\n [apiClient, wsUrl]\n );\n\n const user =\n userName || userEmail ? { name: userName, email: userEmail } : undefined;\n\n return (\n <SupportChatWidget\n adapter={adapter}\n channel={channel}\n institutionId={institutionId || undefined}\n user={user}\n theme={theme}\n faqArticleUrl={faqArticleUrl ?? flag.faqArticleUrl}\n flags={{\n enabled: enabled && !flag.loading && flag.isNative && flag.chatEnabled,\n supportType: flag.isNative ? SupportType.NATIVE : SupportType.ZENDESK,\n }}\n />\n );\n};\n","import { isSupportTicketOrigin } from '../../types/support';\nimport {\n SUPPORT_CHAT_AUTHOR,\n SUPPORT_CHAT_GUEST_SENDER_ID,\n SUPPORT_CHAT_STATUS,\n type SupportChatAdapter,\n type SupportChatAnswerApi,\n type SupportChatArticle,\n type SupportChatEvent,\n type SupportChatHttpClient,\n type SupportChatMessage,\n} from '../../types/supportChat';\n\nexport interface GuestSupportChatAdapterOptions {\n /** Client of the backend API (e.g. the app axios instance) */\n apiClient: SupportChatHttpClient;\n /**\n * Support WebSocket, including ?channel=support (e.g.\n * wss://bff-hml-chat.analyticaensino.com.br?channel=support).\n * Without it the adapter only polls the REST API.\n */\n wsUrl?: string;\n /** Polling interval while the socket is not connected, in ms */\n pollIntervalMs?: number;\n}\n\nconst GUEST_TOKEN_HEADER = 'x-support-guest-token';\n\nconst senderNameFor = (role: string, requesterName: string) => {\n if (role === SUPPORT_CHAT_AUTHOR.AGENT) return 'Suporte';\n if (role === SUPPORT_CHAT_AUTHOR.AI) return 'Assistente';\n return requesterName;\n};\n\n/** REST answer → widget message */\nexport const fromSupportAnswer = (\n answer: SupportChatAnswerApi,\n requesterName: string\n): SupportChatMessage => ({\n id: answer.id,\n senderId:\n answer.authorType === SUPPORT_CHAT_AUTHOR.GUEST\n ? SUPPORT_CHAT_GUEST_SENDER_ID\n : answer.authorType.toLowerCase(),\n senderName: senderNameFor(answer.authorType, requesterName),\n senderPhoto: null,\n senderRole: answer.authorType,\n content: answer.answer,\n messageType: 'text',\n createdAt: answer.createdAt,\n attachments: answer.attachments ?? [],\n faq:\n answer.faqArticleId && answer.faqArticleTitle\n ? { id: answer.faqArticleId, title: answer.faqArticleTitle }\n : null,\n contactRequest: answer.contactRequest ?? null,\n});\n\n/**\n * Support chat adapter for visitors (no login), on top of the support API:\n * - POST /support/external opens the ticket and returns the guest token;\n * - GET/POST /support/guest/:id/answer read and send, with the token header;\n * - the chat WebSocket (?channel=support&roomId=&token=) pushes the events,\n * and REST polling covers the time the socket is down.\n */\nexport const createGuestSupportChatAdapter = ({\n apiClient,\n wsUrl,\n pollIntervalMs = 3000,\n}: GuestSupportChatAdapterOptions): SupportChatAdapter => {\n const sessions = new Map<string, { token: string; name: string }>();\n /**\n * Ticket opening text written by the adapter (an image-only start still\n * needs a description). It is the ticket description for the agents, not\n * something the visitor typed, so the chat does not show it.\n */\n const hiddenIds = new Set<string>();\n const isVisible = (message: SupportChatMessage) => !hiddenIds.has(message.id);\n\n const headersFor = (ticketId: string) => ({\n [GUEST_TOKEN_HEADER]: sessions.get(ticketId)?.token ?? '',\n });\n const nameFor = (ticketId: string) =>\n sessions.get(ticketId)?.name ?? 'Visitante';\n\n const listMessages = async (ticketId: string) => {\n const { data } = await apiClient.get<{ data: SupportChatAnswerApi[] }>(\n `/support/guest/${ticketId}/answer`,\n { headers: headersFor(ticketId) }\n );\n return data.data\n .map((answer) => fromSupportAnswer(answer, nameFor(ticketId)))\n .filter(isVisible);\n };\n\n /**\n * Upload each file straight to the storage: ask the backend for a signed\n * URL, then PUT the file there. Returns what the message needs.\n */\n const uploadFiles = (ticketId: string, files: File[]) =>\n Promise.all(\n files.map(async (file) => {\n const { data } = await apiClient.post<{\n data: { uploadUrl: string; key: string; contentType: string };\n }>(\n `/support/guest/${ticketId}/upload-url`,\n { fileName: file.name, fileSize: file.size, mimeType: file.type },\n { headers: headersFor(ticketId) }\n );\n const { uploadUrl, key, contentType } = data.data;\n const response = await fetch(uploadUrl, {\n method: 'PUT',\n headers: { 'Content-Type': contentType },\n body: file,\n });\n if (!response.ok) {\n throw new Error(`Não foi possível enviar ${file.name}`);\n }\n return {\n key,\n fileName: file.name,\n mimeType: contentType,\n size: file.size,\n };\n })\n );\n\n const sendMessage = async (\n ticketId: string,\n content: string,\n files: File[] = []\n ) => {\n const attachments = await uploadFiles(ticketId, files);\n const { data } = await apiClient.post<{ data: SupportChatAnswerApi }>(\n `/support/guest/${ticketId}/answer`,\n { answer: content, attachments },\n { headers: headersFor(ticketId) }\n );\n return fromSupportAnswer(data.data, nameFor(ticketId));\n };\n\n return {\n async startConversation({\n message,\n channel,\n institutionId,\n requesterName,\n email,\n files = [],\n }) {\n // The ticket needs a description; an image-only start uses the names\n const description = message || files.map((file) => file.name).join(', ');\n const { data } = await apiClient.post<{\n data: { id: string; guestToken: string };\n }>('/support/external', {\n description,\n ...(institutionId ? { institutionId } : {}),\n ...(requesterName ? { requesterName } : {}),\n ...(email ? { email } : {}),\n // The app the chat runs in, when it is one the API knows\n ...(isSupportTicketOrigin(channel) ? { origin: channel } : {}),\n });\n const ticketId = data.data.id;\n sessions.set(ticketId, {\n token: data.data.guestToken,\n name: requesterName || 'Visitante',\n });\n\n // The first message is saved with the ticket; read it back for its id.\n // /support/external takes no files, so they go as the next message.\n let messages = await listMessages(ticketId);\n if (!message && messages[0]) {\n hiddenIds.add(messages[0].id);\n messages = messages.slice(1);\n }\n if (files.length) messages.push(await sendMessage(ticketId, '', files));\n return {\n ticket: {\n id: ticketId,\n status: SUPPORT_CHAT_STATUS.ABERTO,\n handlerType: null,\n problemType: null,\n },\n messages,\n };\n },\n\n sendMessage,\n\n async getArticle(institutionId, articleId) {\n const { data } = await apiClient.get<{\n data: { article: SupportChatArticle };\n }>(`/help-center/${institutionId}/articles/${articleId}`);\n const { id, title, body, updatedAt } = data.data.article;\n return { id, title, body, updatedAt: updatedAt ?? null };\n },\n\n async updateContact(ticketId, contact) {\n if (!apiClient.put) {\n throw new Error('O cliente da API não envia PUT');\n }\n await apiClient.put(`/support/guest/${ticketId}`, contact, {\n headers: headersFor(ticketId),\n });\n const session = sessions.get(ticketId);\n if (session && contact.requesterName) {\n session.name = contact.requesterName;\n }\n },\n\n listMessages,\n\n subscribe(ticketId, listener) {\n // id → what the chat shows besides the text; a message seen again with\n // more of it (a stale WebSocket history may lack contactRequest/faq) is\n // delivered again so the chat replaces it\n const seen = new Map<string, string>();\n const extrasOf = (message: SupportChatMessage) =>\n JSON.stringify([message.contactRequest ?? null, message.faq ?? null]);\n const NO_EXTRAS = JSON.stringify([null, null]);\n let socket: WebSocket | null = null;\n let pollTimer: ReturnType<typeof setInterval> | null = null;\n let active = true;\n\n const deliver = (message: SupportChatMessage) => {\n if (!isVisible(message)) return;\n const extras = extrasOf(message);\n const previous = seen.get(message.id);\n // Same as before, or a poorer copy: nothing new to show\n if (previous === extras || (previous && extras === NO_EXTRAS)) return;\n seen.set(message.id, extras);\n listener({ type: 'new_message', payload: { message } });\n };\n\n const poll = async () => {\n try {\n (await listMessages(ticketId)).forEach(deliver);\n } catch {\n // Next tick tries again\n }\n };\n const startPolling = () => {\n if (pollTimer || !active) return;\n void poll();\n pollTimer = setInterval(poll, pollIntervalMs);\n };\n const stopPolling = () => {\n if (pollTimer) clearInterval(pollTimer);\n pollTimer = null;\n };\n\n const handleFrame = (raw: string) => {\n let event: { type?: string; payload?: Record<string, unknown> };\n try {\n event = JSON.parse(raw);\n } catch {\n return;\n }\n const payload = event.payload ?? {};\n if (event.type === 'history') {\n (payload.messages as SupportChatMessage[]).forEach((message) =>\n deliver({ ...message, attachments: message.attachments ?? [] })\n );\n } else if (event.type === 'new_message') {\n const message = payload.message as SupportChatMessage;\n deliver({ ...message, attachments: message.attachments ?? [] });\n } else if (\n event.type === 'message_updated' ||\n event.type === 'ticket_updated'\n ) {\n listener(event as SupportChatEvent);\n } else if (event.type === 'error') {\n listener({\n type: 'error',\n payload: {\n message: String(payload.message_text ?? payload.message ?? ''),\n },\n });\n }\n };\n\n const token = sessions.get(ticketId)?.token;\n if (wsUrl && token) {\n const url = new URL(wsUrl);\n url.searchParams.set('channel', 'support');\n url.searchParams.set('roomId', ticketId);\n url.searchParams.set('token', token);\n socket = new WebSocket(url.toString());\n socket.onopen = () => {\n stopPolling();\n socket?.send(JSON.stringify({ type: 'getInitialData' }));\n // The REST API is the source of truth: a WebSocket deployed apart\n // from it may send a history without contactRequest/faq\n void poll();\n };\n socket.onmessage = (event) => handleFrame(String(event.data));\n socket.onclose = startPolling;\n socket.onerror = startPolling;\n } else {\n startPolling();\n }\n\n return () => {\n active = false;\n stopPolling();\n if (socket) {\n socket.onclose = null;\n socket.close();\n }\n };\n },\n };\n};\n","import {\n SUPPORT_CHAT_AUTHOR,\n SUPPORT_CHAT_GUEST_SENDER_ID,\n SUPPORT_CHAT_HANDLER,\n SUPPORT_CHAT_STATUS,\n type SupportChatAdapter,\n type SupportChatEvent,\n type SupportChatMessage,\n type SupportChatTicket,\n} from '../../types/supportChat';\n\nexport interface MockSupportChatAdapterOptions {\n /** Latency of every call, in ms (default 300) */\n delayMs?: number;\n /** Delay of the automatic agent reply, in ms (default 1500) */\n replyDelayMs?: number;\n /** Text of the agent reply; null disables it */\n replyText?: string | null;\n}\n\nconst DEFAULT_REPLY =\n 'Olá! Recebemos sua mensagem e já vamos te ajudar. Este é um atendimento de demonstração.';\n\n/**\n * In-memory adapter for the support chat widget. It mirrors the backend\n * contract (statuses, authors, events) so the widget can be built and tested\n * before the real API + WebSocket adapter exists.\n */\nexport const createMockSupportChatAdapter = ({\n delayMs = 300,\n replyDelayMs = 1500,\n replyText = DEFAULT_REPLY,\n}: MockSupportChatAdapterOptions = {}): SupportChatAdapter => {\n const tickets = new Map<string, SupportChatTicket>();\n const messages = new Map<string, SupportChatMessage[]>();\n const requesters = new Map<string, { name: string; logged: boolean }>();\n const listeners = new Map<string, Set<(event: SupportChatEvent) => void>>();\n let sequence = 0;\n\n const wait = () => new Promise((resolve) => setTimeout(resolve, delayMs));\n const nextId = (prefix: string) => `${prefix}-${++sequence}`;\n\n const emit = (ticketId: string, event: SupportChatEvent) => {\n listeners.get(ticketId)?.forEach((listener) => listener(event));\n };\n\n const append = (ticketId: string, message: SupportChatMessage) => {\n messages.set(ticketId, [...(messages.get(ticketId) ?? []), message]);\n };\n\n const toAttachments = (files: File[]) =>\n files.map((file) => ({\n id: nextId('file'),\n url: URL.createObjectURL(file),\n fileName: file.name,\n mimeType: file.type,\n size: file.size,\n }));\n\n const buildRequesterMessage = (\n ticketId: string,\n content: string,\n files: File[] = []\n ): SupportChatMessage => {\n const requester = requesters.get(ticketId);\n return {\n id: nextId('msg'),\n senderId: requester?.logged ? 'user' : SUPPORT_CHAT_GUEST_SENDER_ID,\n senderName: requester?.name ?? 'Visitante',\n senderPhoto: null,\n senderRole: requester?.logged\n ? SUPPORT_CHAT_AUTHOR.USER\n : SUPPORT_CHAT_AUTHOR.GUEST,\n content,\n messageType: 'text',\n createdAt: new Date().toISOString(),\n attachments: toAttachments(files),\n };\n };\n\n const scheduleReply = (ticketId: string) => {\n if (replyText === null) return;\n setTimeout(() => {\n const ticket = tickets.get(ticketId);\n if (!ticket) return;\n\n const reply: SupportChatMessage = {\n id: nextId('msg'),\n senderId: 'agent',\n senderName: 'Suporte',\n senderPhoto: null,\n senderRole: SUPPORT_CHAT_AUTHOR.AGENT,\n content: replyText,\n messageType: 'text',\n createdAt: new Date().toISOString(),\n attachments: [],\n };\n append(ticketId, reply);\n\n const updated: SupportChatTicket = {\n ...ticket,\n status: SUPPORT_CHAT_STATUS.EM_ANDAMENTO,\n handlerType: SUPPORT_CHAT_HANDLER.HUMANO,\n };\n tickets.set(ticketId, updated);\n\n emit(ticketId, { type: 'new_message', payload: { message: reply } });\n emit(ticketId, {\n type: 'ticket_updated',\n payload: {\n ticketId,\n status: updated.status,\n handlerType: updated.handlerType,\n problemType: updated.problemType,\n },\n });\n }, replyDelayMs);\n };\n\n return {\n async getArticle(_institutionId, articleId) {\n await wait();\n return {\n id: articleId,\n title: 'Como gerar e localizar simulados',\n body:\n '<p>Monte um simulado com as matérias que quiser e acompanhe o resultado. 📝</p>' +\n '<h2>Passo a passo</h2>' +\n '<ol><li><p>No menu, abra <strong>Simulados</strong>.</p></li>' +\n '<li><p>Clique em <strong>Gerar simulado</strong> e escolha as áreas.</p></li>' +\n '<li><p>Responda e veja o resultado em <strong>Histórico</strong>.</p></li></ol>' +\n '<p><a href=\"https://www.youtube.com/watch?v=dQw4w9WgXcQ\">Assista ao vídeo do passo a passo</a></p>' +\n '<blockquote><p>💡 Dica: refaça as questões que errou.</p></blockquote>',\n updatedAt: '2026-10-21T12:00:00.000Z',\n };\n },\n\n async startConversation({ message, requesterName, email, files }) {\n await wait();\n const ticket: SupportChatTicket = {\n id: nextId('ticket'),\n status: SUPPORT_CHAT_STATUS.ABERTO,\n handlerType: null,\n problemType: null,\n };\n tickets.set(ticket.id, ticket);\n requesters.set(ticket.id, {\n name: requesterName || 'Visitante',\n logged: Boolean(email),\n });\n\n const first = buildRequesterMessage(ticket.id, message, files);\n append(ticket.id, first);\n scheduleReply(ticket.id);\n return { ticket, messages: [first] };\n },\n\n async sendMessage(ticketId, content, files) {\n await wait();\n const ticket = tickets.get(ticketId);\n if (!ticket) throw new Error('Atendimento não encontrado');\n if (ticket.status === SUPPORT_CHAT_STATUS.FECHADO) {\n throw new Error('Este atendimento já foi encerrado');\n }\n\n const message = buildRequesterMessage(ticketId, content, files);\n append(ticketId, message);\n scheduleReply(ticketId);\n return message;\n },\n\n async listMessages(ticketId) {\n await wait();\n return [...(messages.get(ticketId) ?? [])];\n },\n\n subscribe(ticketId, listener) {\n const set = listeners.get(ticketId) ?? new Set();\n set.add(listener);\n listeners.set(ticketId, set);\n return () => {\n set.delete(listener);\n };\n },\n };\n};\n"],"mappings":"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","names":["useCallback","useEffect","useRef","useState","XIcon","useState","useState","useEffect","useRef","useState","jsx","jsxs","useState","useRef","useEffect","title","useEffect","useState","jsx","jsxs","useState","useEffect","jsx","jsxs","useState","useRef","useCallback","useEffect","XIcon","useMemo","jsx","useMemo"]}
|
|
@@ -36,4 +36,4 @@ var SUPPORT_HISTORY_FILTER = {
|
|
|
36
36
|
|
|
37
37
|
|
|
38
38
|
exports.SUPPORT_USER_PANEL_TAB = SUPPORT_USER_PANEL_TAB; exports.SUPPORT_CHANNEL = SUPPORT_CHANNEL; exports.SUPPORT_TICKET_PRIORITY = SUPPORT_TICKET_PRIORITY; exports.SUPPORT_TICKET_CARD_STATUS = SUPPORT_TICKET_CARD_STATUS; exports.SUPPORT_HISTORY_FILTER = SUPPORT_HISTORY_FILTER;
|
|
39
|
-
//# sourceMappingURL=chunk-
|
|
39
|
+
//# sourceMappingURL=chunk-OGD2ATAP.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/home/runner/work/analytica-frontend-lib/analytica-frontend-lib/dist/chunk-
|
|
1
|
+
{"version":3,"sources":["/home/runner/work/analytica-frontend-lib/analytica-frontend-lib/dist/chunk-OGD2ATAP.js","../src/types/supportUserPanel.ts"],"names":[],"mappings":"AAAA;ACSO,IAAM,uBAAA,EAAyB;AAAA,EACpC,OAAA,EAAS,SAAA;AAAA,EACT,OAAA,EAAS,SAAA;AAAA,EACT,MAAA,EAAQ;AACV,CAAA;AAMO,IAAM,gBAAA,EAAkB;AAAA,EAC7B,GAAA,EAAK,KAAA;AAAA,EACL,GAAA,EAAK,KAAA;AAAA,EACL,QAAA,EAAU,UAAA;AAAA,EACV,KAAA,EAAO;AACT,CAAA;AAMO,IAAM,wBAAA,EAA0B;AAAA,EACrC,GAAA,EAAK,KAAA;AAAA,EACL,MAAA,EAAQ,QAAA;AAAA,EACR,IAAA,EAAM,MAAA;AAAA,EACN,MAAA,EAAQ;AACV,CAAA;AAMO,IAAM,2BAAA,EAA6B;AAAA,EACxC,WAAA,EAAa,aAAA;AAAA,EACb,aAAA,EAAe,eAAA;AAAA,EACf,KAAA,EAAO,OAAA;AAAA,EACP,IAAA,EAAM;AACR,CAAA;AAMO,IAAM,uBAAA,EAAyB;AAAA,EACpC,GAAA,EAAK,KAAA;AAAA,EACL,IAAA,EAAM,MAAA;AAAA,EACN,IAAA,EAAM,MAAA;AAAA,EACN,EAAA,EAAI;AACN,CAAA;AD3BA;AACA;AACE;AACA;AACA;AACA;AACA;AACF,wRAAC","file":"/home/runner/work/analytica-frontend-lib/analytica-frontend-lib/dist/chunk-OGD2ATAP.js","sourcesContent":[null,"import type { SupportTimelineStatus } from './supportTimeline';\n\n/**\n * Support (SAC) user panel types — right-side panel of the agent's ticket\n * screen, with the contact profile, their ticket history and the current\n * ticket details.\n */\n\n/** Tabs of the user panel */\nexport const SUPPORT_USER_PANEL_TAB = {\n PROFILE: 'profile',\n HISTORY: 'history',\n TICKET: 'ticket',\n} as const;\n\nexport type SupportUserPanelTab =\n (typeof SUPPORT_USER_PANEL_TAB)[keyof typeof SUPPORT_USER_PANEL_TAB];\n\n/** Channel a contact reached support through */\nexport const SUPPORT_CHANNEL = {\n APP: 'App',\n WEB: 'Web',\n WHATSAPP: 'WhatsApp',\n EMAIL: 'E-mail',\n} as const;\n\nexport type SupportChannel =\n (typeof SUPPORT_CHANNEL)[keyof typeof SUPPORT_CHANNEL];\n\n/** Ticket priority */\nexport const SUPPORT_TICKET_PRIORITY = {\n LOW: 'low',\n NORMAL: 'normal',\n HIGH: 'high',\n URGENT: 'urgent',\n} as const;\n\nexport type SupportTicketPriority =\n (typeof SUPPORT_TICKET_PRIORITY)[keyof typeof SUPPORT_TICKET_PRIORITY];\n\n/** Status shown on a ticket card of the history tab */\nexport const SUPPORT_TICKET_CARD_STATUS = {\n IN_PROGRESS: 'IN_PROGRESS',\n WAITING_HUMAN: 'WAITING_HUMAN',\n HUMAN: 'HUMAN',\n DONE: 'DONE',\n} as const;\n\nexport type SupportTicketCardStatus =\n (typeof SUPPORT_TICKET_CARD_STATUS)[keyof typeof SUPPORT_TICKET_CARD_STATUS];\n\n/** Filters of the history tab (single selection) */\nexport const SUPPORT_HISTORY_FILTER = {\n ALL: 'all',\n OPEN: 'open',\n DONE: 'done',\n AI: 'ai',\n} as const;\n\nexport type SupportHistoryFilter =\n (typeof SUPPORT_HISTORY_FILTER)[keyof typeof SUPPORT_HISTORY_FILTER];\n\n/** Note left by an agent on a contact, visible to the whole team */\nexport interface SupportContactNote {\n id: string;\n text: string;\n author: string;\n /** ISO date */\n createdAt: string;\n}\n\n/** Contact (the user who opened the ticket) */\nexport interface SupportContact {\n id: string;\n name: string;\n avatarUrl?: string | null;\n /** e.g. \"Estudante\" */\n role: string;\n /** e.g. \"Rede Aprender Mais\" */\n institution: string;\n /** Whether the contact is linked to a platform user */\n linked: boolean;\n email?: string | null;\n /** Digits only or already formatted; rendered as `(DD) 9XXXX-XXXX` */\n phone?: string | null;\n /** e.g. \"3º ano B — Ensino Médio\" */\n classOrPosition?: string | null;\n /** ISO date */\n firstContactAt: string;\n /** ISO date */\n lastContactAt: string;\n ticketsCount: number;\n /** e.g. \"Android 14 · Moto G54\" */\n device?: string | null;\n preferredChannel: SupportChannel;\n notes: SupportContactNote[];\n}\n\n/** Who resolved a ticket */\nexport type SupportTicketResolver =\n | { type: 'ai' }\n | { type: 'agent'; name: string };\n\n/**\n * Id of a support ticket: a number (e.g. 4821) or a UUID. When it is not\n * meant for display, give the ticket a `code`.\n */\nexport type SupportTicketId = number | string;\n\n/** Ticket card of the history tab */\nexport interface SupportTicketSummary {\n id: SupportTicketId;\n /** Shown on the card; defaults to `#{id}` (e.g. \"#3F4A5B\" for a UUID) */\n code?: string;\n title: string;\n reason?: string | null;\n subreason?: string | null;\n status: SupportTicketCardStatus;\n /** ISO date */\n createdAt: string;\n resolvedBy?: SupportTicketResolver;\n durationMin?: number | null;\n /** Shown when the card is expanded */\n priority?: SupportTicketPriority | null;\n /** Shown when the card is expanded */\n channel?: SupportChannel | null;\n /** App the ticket was opened from, already as a label (e.g. \"Professor\") */\n origin?: string | null;\n}\n\n/** Aggregated numbers shown above the history list */\nexport interface SupportHistorySummary {\n total: number;\n resolved: number;\n /** Average resolution time in minutes; null when nothing was resolved */\n avgMin: number | null;\n}\n\n/** Status change event of the current ticket */\nexport interface SupportTicketTimelineEvent {\n status: SupportTimelineStatus;\n /** ISO date */\n at: string;\n}\n\n/** Option of the reason select */\nexport interface SupportReasonOption {\n value: string;\n label: string;\n}\n\n/** Current ticket details */\nexport interface SupportTicketDetails {\n id: SupportTicketId;\n channel: SupportChannel;\n /**\n * App the ticket was opened from, already as a label (e.g. \"Professor\").\n * Shown next to the channel (\"Web · Professor\").\n */\n origin?: string | null;\n /** ISO date */\n openedAt: string;\n /** ISO date of the first human reply; absent while waiting for a human */\n firstHumanAt?: string | null;\n /** ISO date the ticket was concluded; absent while it is open */\n closedAt?: string | null;\n /** Whether the ticket is concluded (fields become read-only, counters stop) */\n done: boolean;\n /** Value of the selected reason, from `reasonOptions` */\n reason?: string | null;\n priority: SupportTicketPriority;\n aiSummary?: string | null;\n timeline: SupportTicketTimelineEvent[];\n}\n"]}
|
|
@@ -2,7 +2,7 @@ import {
|
|
|
2
2
|
getCategoryText,
|
|
3
3
|
getStatusBadgeAction,
|
|
4
4
|
getStatusText
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-MVT7IVMY.mjs";
|
|
6
6
|
import {
|
|
7
7
|
SkeletonText
|
|
8
8
|
} from "./chunk-7K5SWZK5.mjs";
|
|
@@ -434,4 +434,4 @@ export {
|
|
|
434
434
|
getCategoryIcon,
|
|
435
435
|
TicketModal
|
|
436
436
|
};
|
|
437
|
-
//# sourceMappingURL=chunk-
|
|
437
|
+
//# sourceMappingURL=chunk-VKQ5AJDT.mjs.map
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
|
|
4
4
|
|
|
5
|
-
var
|
|
5
|
+
var _chunk64WFAQC6js = require('./chunk-64WFAQC6.js');
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
var _chunkUJWCE4XPjs = require('./chunk-UJWCE4XP.js');
|
|
@@ -200,9 +200,9 @@ var TicketModal = ({
|
|
|
200
200
|
{
|
|
201
201
|
variant: "solid",
|
|
202
202
|
size: "small",
|
|
203
|
-
action:
|
|
203
|
+
action: _chunk64WFAQC6js.getStatusBadgeAction.call(void 0, ticket.status),
|
|
204
204
|
className: "w-fit",
|
|
205
|
-
children:
|
|
205
|
+
children: _chunk64WFAQC6js.getStatusText.call(void 0, ticket.status)
|
|
206
206
|
}
|
|
207
207
|
)
|
|
208
208
|
] }),
|
|
@@ -225,7 +225,7 @@ var TicketModal = ({
|
|
|
225
225
|
className: "w-fit",
|
|
226
226
|
children: [
|
|
227
227
|
getCategoryIcon(ticket.category),
|
|
228
|
-
|
|
228
|
+
_chunk64WFAQC6js.getCategoryText.call(void 0, ticket.category)
|
|
229
229
|
]
|
|
230
230
|
}
|
|
231
231
|
)
|
|
@@ -434,4 +434,4 @@ var TicketModal = ({
|
|
|
434
434
|
|
|
435
435
|
|
|
436
436
|
exports.getCategoryIcon = getCategoryIcon; exports.TicketModal = TicketModal;
|
|
437
|
-
//# sourceMappingURL=chunk-
|
|
437
|
+
//# sourceMappingURL=chunk-VQ7MSYIH.js.map
|