@greatapps/greatchat-ui 0.1.0 → 0.1.1
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/index.d.ts +43 -1
- package/dist/index.js +881 -1
- package/dist/index.js.map +1 -1
- package/package.json +3 -1
- package/src/components/chat-input.tsx +54 -0
- package/src/components/chat-view.tsx +135 -0
- package/src/components/index.ts +8 -0
- package/src/components/message-bubble.tsx +394 -0
- package/src/components/ui/alert-dialog.tsx +167 -0
- package/src/components/ui/badge.tsx +44 -0
- package/src/components/ui/button.tsx +62 -0
- package/src/components/ui/dropdown-menu.tsx +173 -0
- package/src/components/ui/select.tsx +156 -0
- package/src/components/ui/skeleton.tsx +16 -0
- package/src/components/ui/textarea.tsx +18 -0
- package/src/index.ts +5 -0
- package/src/utils/format-date.ts +13 -0
- package/src/utils/group-messages.ts +22 -0
- package/src/utils/index.ts +2 -0
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/client/index.ts","../src/lib/utils.ts"],"sourcesContent":["import type {\n ApiResponse,\n Channel,\n Contact,\n Inbox,\n InboxMessage,\n InboxStats,\n WhatsappStatus,\n} from \"../types\";\n\nexport interface GchatClientConfig {\n baseUrl: string;\n token: string;\n language?: string;\n idWl?: number;\n}\n\nexport function createGchatClient(config: GchatClientConfig) {\n const { baseUrl, token, language = \"pt-br\", idWl = 1 } = config;\n\n function buildUrl(idAccount: number, path: string): string {\n return `${baseUrl}/v1/${language}/${idWl}/accounts/${idAccount}/${path}`;\n }\n\n async function request<T>(\n method: string,\n idAccount: number,\n path: string,\n body?: unknown,\n params?: Record<string, string>,\n ): Promise<ApiResponse<T>> {\n const url = new URL(buildUrl(idAccount, path));\n if (params) {\n Object.entries(params).forEach(([k, v]) => url.searchParams.set(k, v));\n }\n\n const res = await fetch(url.toString(), {\n method,\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${token}`,\n },\n body: body ? JSON.stringify(body) : undefined,\n });\n\n if (!res.ok) {\n let message = `HTTP ${res.status}: ${res.statusText}`;\n try {\n const errorBody = await res.json();\n message = errorBody.message || message;\n } catch {\n /* ignore parse errors */\n }\n throw new Error(message);\n }\n\n const json: ApiResponse<T> = await res.json();\n\n // For mutations, treat API-level failures as errors so TanStack Query\n // fires onError instead of onSuccess (API returns HTTP 200 even on failure)\n if (method !== \"GET\" && json.status === 0) {\n throw new Error(json.message || \"Operação falhou\");\n }\n\n return json;\n }\n\n return {\n // --- Inboxes ---\n listInboxes: (idAccount: number, params?: Record<string, string>) =>\n request<Inbox[]>(\"GET\", idAccount, \"inboxes\", undefined, params),\n getInbox: (idAccount: number, id: number) =>\n request<Inbox>(\"GET\", idAccount, `inboxes/${id}`),\n getInboxStats: (idAccount: number) =>\n request<InboxStats>(\"GET\", idAccount, \"inboxes/stats\"),\n createInbox: (\n idAccount: number,\n body: { id_channel: number; id_contact: number },\n ) => request<Inbox>(\"POST\", idAccount, \"inboxes\", body),\n updateInbox: (\n idAccount: number,\n id: number,\n body: Partial<Pick<Inbox, \"status\" | \"id_agent\">>,\n ) => request<Inbox>(\"PUT\", idAccount, `inboxes/${id}`, body),\n deleteInbox: (idAccount: number, id: number) =>\n request<void>(\"DELETE\", idAccount, `inboxes/${id}`),\n\n // --- Inbox Messages ---\n listInboxMessages: (idAccount: number, params: Record<string, string>) =>\n request<InboxMessage[]>(\n \"GET\",\n idAccount,\n \"inbox-messages\",\n undefined,\n params,\n ),\n sendMessage: (\n idAccount: number,\n body: {\n id_inbox: number;\n content: string;\n content_type?: string;\n source?: string;\n direction: \"outbound\";\n },\n ) => request<InboxMessage>(\"POST\", idAccount, \"inbox-messages\", body),\n revokeMessage: (idAccount: number, id: number) =>\n request<void>(\"POST\", idAccount, `inbox-messages/${id}/revoke`),\n editMessage: (idAccount: number, id: number, body: { content: string }) =>\n request<void>(\"POST\", idAccount, `inbox-messages/${id}/edit`, body),\n\n // --- Contacts ---\n listContacts: (idAccount: number, params?: Record<string, string>) =>\n request<Contact[]>(\"GET\", idAccount, \"contacts\", undefined, params),\n getContact: (idAccount: number, id: number) =>\n request<Contact>(\"GET\", idAccount, `contacts/${id}`),\n createContact: (\n idAccount: number,\n body: Pick<Contact, \"name\"> &\n Partial<Pick<Contact, \"phone_number\" | \"identifier\">>,\n ) => request<Contact>(\"POST\", idAccount, \"contacts\", body),\n updateContact: (\n idAccount: number,\n id: number,\n body: Partial<Pick<Contact, \"name\" | \"phone_number\" | \"identifier\">>,\n ) => request<Contact>(\"PUT\", idAccount, `contacts/${id}`, body),\n deleteContact: (idAccount: number, id: number) =>\n request<void>(\"DELETE\", idAccount, `contacts/${id}`),\n\n // --- Channels ---\n listChannels: (idAccount: number) =>\n request<Channel[]>(\"GET\", idAccount, \"channels\"),\n getChannel: (idAccount: number, id: number) =>\n request<Channel>(\"GET\", idAccount, `channels/${id}`),\n createChannel: (\n idAccount: number,\n body: Pick<Channel, \"name\" | \"type\" | \"provider\"> &\n Partial<Pick<Channel, \"identifier\" | \"id_agent\">>,\n ) => request<Channel>(\"POST\", idAccount, \"channels\", body),\n updateChannel: (\n idAccount: number,\n id: number,\n body: Partial<\n Pick<Channel, \"name\" | \"identifier\" | \"status\" | \"id_agent\">\n >,\n ) => request<Channel>(\"PUT\", idAccount, `channels/${id}`, body),\n deleteChannel: (idAccount: number, id: number) =>\n request<void>(\"DELETE\", idAccount, `channels/${id}`),\n\n // --- WhatsApp Channel Management ---\n connectChannel: (idAccount: number, id: number) =>\n request<unknown>(\"POST\", idAccount, `channels/${id}/connect`),\n getChannelQR: async (\n idAccount: number,\n id: number,\n ): Promise<string | null> => {\n const url = buildUrl(idAccount, `channels/${id}/qr`);\n const res = await fetch(url, {\n headers: {\n Accept: \"image/png\",\n Authorization: `Bearer ${token}`,\n },\n });\n\n if (!res.ok) return null;\n\n const contentType = res.headers.get(\"content-type\") || \"\";\n if (!contentType.includes(\"image/\")) return null;\n\n const blob = await res.blob();\n return new Promise<string>((resolve, reject) => {\n const reader = new FileReader();\n reader.onloadend = () => resolve(reader.result as string);\n reader.onerror = () => reject(new Error(\"Failed to read QR image\"));\n reader.readAsDataURL(blob);\n });\n },\n getChannelWhatsappStatus: (idAccount: number, id: number) =>\n request<WhatsappStatus>(\n \"GET\",\n idAccount,\n `channels/${id}/whatsapp-status`,\n ),\n disconnectChannel: (idAccount: number, id: number) =>\n request<unknown>(\"POST\", idAccount, `channels/${id}/disconnect`),\n logoutChannel: (idAccount: number, id: number) =>\n request<unknown>(\"POST\", idAccount, `channels/${id}/logout`),\n provisionChannel: (idAccount: number, id: number) =>\n request<unknown>(\"POST\", idAccount, `channels/${id}/provision`),\n };\n}\n","import { clsx, type ClassValue } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs));\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/client/index.ts","../src/lib/utils.ts","../src/utils/group-messages.ts","../src/utils/format-date.ts","../src/components/chat-view.tsx","../src/components/message-bubble.tsx","../src/components/ui/dropdown-menu.tsx","../src/components/ui/alert-dialog.tsx","../src/components/ui/button.tsx","../src/components/ui/textarea.tsx","../src/components/chat-input.tsx","../src/components/ui/skeleton.tsx","../src/components/ui/select.tsx"],"sourcesContent":["import type {\n ApiResponse,\n Channel,\n Contact,\n Inbox,\n InboxMessage,\n InboxStats,\n WhatsappStatus,\n} from \"../types\";\n\nexport interface GchatClientConfig {\n baseUrl: string;\n token: string;\n language?: string;\n idWl?: number;\n}\n\nexport function createGchatClient(config: GchatClientConfig) {\n const { baseUrl, token, language = \"pt-br\", idWl = 1 } = config;\n\n function buildUrl(idAccount: number, path: string): string {\n return `${baseUrl}/v1/${language}/${idWl}/accounts/${idAccount}/${path}`;\n }\n\n async function request<T>(\n method: string,\n idAccount: number,\n path: string,\n body?: unknown,\n params?: Record<string, string>,\n ): Promise<ApiResponse<T>> {\n const url = new URL(buildUrl(idAccount, path));\n if (params) {\n Object.entries(params).forEach(([k, v]) => url.searchParams.set(k, v));\n }\n\n const res = await fetch(url.toString(), {\n method,\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${token}`,\n },\n body: body ? JSON.stringify(body) : undefined,\n });\n\n if (!res.ok) {\n let message = `HTTP ${res.status}: ${res.statusText}`;\n try {\n const errorBody = await res.json();\n message = errorBody.message || message;\n } catch {\n /* ignore parse errors */\n }\n throw new Error(message);\n }\n\n const json: ApiResponse<T> = await res.json();\n\n // For mutations, treat API-level failures as errors so TanStack Query\n // fires onError instead of onSuccess (API returns HTTP 200 even on failure)\n if (method !== \"GET\" && json.status === 0) {\n throw new Error(json.message || \"Operação falhou\");\n }\n\n return json;\n }\n\n return {\n // --- Inboxes ---\n listInboxes: (idAccount: number, params?: Record<string, string>) =>\n request<Inbox[]>(\"GET\", idAccount, \"inboxes\", undefined, params),\n getInbox: (idAccount: number, id: number) =>\n request<Inbox>(\"GET\", idAccount, `inboxes/${id}`),\n getInboxStats: (idAccount: number) =>\n request<InboxStats>(\"GET\", idAccount, \"inboxes/stats\"),\n createInbox: (\n idAccount: number,\n body: { id_channel: number; id_contact: number },\n ) => request<Inbox>(\"POST\", idAccount, \"inboxes\", body),\n updateInbox: (\n idAccount: number,\n id: number,\n body: Partial<Pick<Inbox, \"status\" | \"id_agent\">>,\n ) => request<Inbox>(\"PUT\", idAccount, `inboxes/${id}`, body),\n deleteInbox: (idAccount: number, id: number) =>\n request<void>(\"DELETE\", idAccount, `inboxes/${id}`),\n\n // --- Inbox Messages ---\n listInboxMessages: (idAccount: number, params: Record<string, string>) =>\n request<InboxMessage[]>(\n \"GET\",\n idAccount,\n \"inbox-messages\",\n undefined,\n params,\n ),\n sendMessage: (\n idAccount: number,\n body: {\n id_inbox: number;\n content: string;\n content_type?: string;\n source?: string;\n direction: \"outbound\";\n },\n ) => request<InboxMessage>(\"POST\", idAccount, \"inbox-messages\", body),\n revokeMessage: (idAccount: number, id: number) =>\n request<void>(\"POST\", idAccount, `inbox-messages/${id}/revoke`),\n editMessage: (idAccount: number, id: number, body: { content: string }) =>\n request<void>(\"POST\", idAccount, `inbox-messages/${id}/edit`, body),\n\n // --- Contacts ---\n listContacts: (idAccount: number, params?: Record<string, string>) =>\n request<Contact[]>(\"GET\", idAccount, \"contacts\", undefined, params),\n getContact: (idAccount: number, id: number) =>\n request<Contact>(\"GET\", idAccount, `contacts/${id}`),\n createContact: (\n idAccount: number,\n body: Pick<Contact, \"name\"> &\n Partial<Pick<Contact, \"phone_number\" | \"identifier\">>,\n ) => request<Contact>(\"POST\", idAccount, \"contacts\", body),\n updateContact: (\n idAccount: number,\n id: number,\n body: Partial<Pick<Contact, \"name\" | \"phone_number\" | \"identifier\">>,\n ) => request<Contact>(\"PUT\", idAccount, `contacts/${id}`, body),\n deleteContact: (idAccount: number, id: number) =>\n request<void>(\"DELETE\", idAccount, `contacts/${id}`),\n\n // --- Channels ---\n listChannels: (idAccount: number) =>\n request<Channel[]>(\"GET\", idAccount, \"channels\"),\n getChannel: (idAccount: number, id: number) =>\n request<Channel>(\"GET\", idAccount, `channels/${id}`),\n createChannel: (\n idAccount: number,\n body: Pick<Channel, \"name\" | \"type\" | \"provider\"> &\n Partial<Pick<Channel, \"identifier\" | \"id_agent\">>,\n ) => request<Channel>(\"POST\", idAccount, \"channels\", body),\n updateChannel: (\n idAccount: number,\n id: number,\n body: Partial<\n Pick<Channel, \"name\" | \"identifier\" | \"status\" | \"id_agent\">\n >,\n ) => request<Channel>(\"PUT\", idAccount, `channels/${id}`, body),\n deleteChannel: (idAccount: number, id: number) =>\n request<void>(\"DELETE\", idAccount, `channels/${id}`),\n\n // --- WhatsApp Channel Management ---\n connectChannel: (idAccount: number, id: number) =>\n request<unknown>(\"POST\", idAccount, `channels/${id}/connect`),\n getChannelQR: async (\n idAccount: number,\n id: number,\n ): Promise<string | null> => {\n const url = buildUrl(idAccount, `channels/${id}/qr`);\n const res = await fetch(url, {\n headers: {\n Accept: \"image/png\",\n Authorization: `Bearer ${token}`,\n },\n });\n\n if (!res.ok) return null;\n\n const contentType = res.headers.get(\"content-type\") || \"\";\n if (!contentType.includes(\"image/\")) return null;\n\n const blob = await res.blob();\n return new Promise<string>((resolve, reject) => {\n const reader = new FileReader();\n reader.onloadend = () => resolve(reader.result as string);\n reader.onerror = () => reject(new Error(\"Failed to read QR image\"));\n reader.readAsDataURL(blob);\n });\n },\n getChannelWhatsappStatus: (idAccount: number, id: number) =>\n request<WhatsappStatus>(\n \"GET\",\n idAccount,\n `channels/${id}/whatsapp-status`,\n ),\n disconnectChannel: (idAccount: number, id: number) =>\n request<unknown>(\"POST\", idAccount, `channels/${id}/disconnect`),\n logoutChannel: (idAccount: number, id: number) =>\n request<unknown>(\"POST\", idAccount, `channels/${id}/logout`),\n provisionChannel: (idAccount: number, id: number) =>\n request<unknown>(\"POST\", idAccount, `channels/${id}/provision`),\n };\n}\n","import { clsx, type ClassValue } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs));\n}\n","import type { InboxMessage } from \"../types\";\n\n/**\n * Groups messages by date. Assumes messages are pre-sorted by datetime_add (ascending).\n */\nexport function groupMessagesByDate(\n messages: InboxMessage[],\n): { date: string; messages: InboxMessage[] }[] {\n const groups: { date: string; messages: InboxMessage[] }[] = [];\n let currentDate = \"\";\n\n for (const msg of messages) {\n const dateStr = msg.datetime_add.split(\"T\")[0];\n if (dateStr !== currentDate) {\n currentDate = dateStr;\n groups.push({ date: msg.datetime_add, messages: [] });\n }\n groups[groups.length - 1].messages.push(msg);\n }\n\n return groups;\n}\n","import { format, isToday, isYesterday } from \"date-fns\";\nimport { ptBR } from \"date-fns/locale\";\n\nexport function formatDateGroup(dateStr: string): string {\n const date = new Date(dateStr);\n if (isToday(date)) return \"Hoje\";\n if (isYesterday(date)) return \"Ontem\";\n return format(date, \"dd 'de' MMMM\", { locale: ptBR });\n}\n\nexport function formatMessageTime(dateStr: string): string {\n return format(new Date(dateStr), \"HH:mm\");\n}\n","import { useRef, useEffect } from \"react\";\nimport type { InboxMessage } from \"../types\";\nimport { groupMessagesByDate } from \"../utils/group-messages\";\nimport { formatDateGroup } from \"../utils/format-date\";\nimport { MessageBubble } from \"./message-bubble\";\nimport { ChatInput } from \"./chat-input\";\nimport { Skeleton } from \"./ui/skeleton\";\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from \"./ui/select\";\n\nexport interface ChatViewProps {\n messages: InboxMessage[];\n isLoading: boolean;\n inboxStatus?: string;\n onStatusChange?: (status: string) => void;\n onSend: (content: string) => void;\n onRetry?: (message: InboxMessage) => void;\n onRevoke?: (message: InboxMessage) => void;\n onEdit?: (message: InboxMessage, newContent: string) => void;\n sendDisabled?: boolean;\n renderHeader?: React.ReactNode;\n renderStatusDropdown?: React.ReactNode;\n}\n\nexport function ChatView({\n messages,\n isLoading,\n inboxStatus,\n onStatusChange,\n onSend,\n onRetry,\n onRevoke,\n onEdit,\n sendDisabled,\n renderHeader,\n renderStatusDropdown,\n}: ChatViewProps) {\n const scrollRef = useRef<HTMLDivElement>(null);\n const prevMessageCount = useRef(0);\n\n useEffect(() => {\n if (messages && messages.length > prevMessageCount.current) {\n const el = scrollRef.current;\n if (el) {\n setTimeout(() => {\n el.scrollTop = el.scrollHeight;\n }, 50);\n }\n }\n prevMessageCount.current = messages?.length || 0;\n }, [messages]);\n\n const groups = messages ? groupMessagesByDate(messages) : [];\n\n return (\n <div className=\"flex h-full flex-col\">\n {/* Header */}\n {renderHeader !== undefined ? (\n renderHeader\n ) : (\n <div className=\"flex items-center justify-between border-b px-4 py-3\">\n <div className=\"flex-1\" />\n {renderStatusDropdown !== undefined ? (\n renderStatusDropdown\n ) : onStatusChange ? (\n <Select\n value={inboxStatus}\n onValueChange={onStatusChange}\n >\n <SelectTrigger className=\"w-[130px] h-8 text-xs\">\n <SelectValue placeholder=\"Status\" />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"open\">Aberta</SelectItem>\n <SelectItem value=\"pending\">Pendente</SelectItem>\n <SelectItem value=\"resolved\">Resolvida</SelectItem>\n </SelectContent>\n </Select>\n ) : null}\n </div>\n )}\n\n {/* Messages */}\n <div ref={scrollRef} className=\"flex-1 overflow-y-auto p-4\">\n {isLoading ? (\n <div className=\"space-y-4\">\n {Array.from({ length: 4 }).map((_, i) => (\n <div\n key={i}\n className={`flex ${i % 2 === 0 ? \"justify-start\" : \"justify-end\"}`}\n >\n <Skeleton className=\"h-12 w-64 rounded-lg\" />\n </div>\n ))}\n </div>\n ) : groups.length === 0 ? (\n <div className=\"flex h-full items-center justify-center text-sm text-muted-foreground\">\n Nenhuma mensagem ainda\n </div>\n ) : (\n <div className=\"space-y-4\">\n {groups.map((group, gi) => (\n <div key={gi}>\n <div className=\"mb-3 flex justify-center\">\n <span className=\"rounded-full bg-muted px-3 py-1 text-[11px] text-muted-foreground\">\n {formatDateGroup(group.date)}\n </span>\n </div>\n <div className=\"space-y-1\">\n {group.messages.map((msg) => (\n <MessageBubble\n key={msg.id}\n message={msg}\n onRetry={onRetry}\n onRevoke={onRevoke}\n onEdit={onEdit}\n />\n ))}\n </div>\n </div>\n ))}\n </div>\n )}\n </div>\n\n {/* Input */}\n <ChatInput onSend={onSend} disabled={sendDisabled} />\n </div>\n );\n}\n","import { useState } from \"react\";\nimport type { InboxMessage } from \"../types\";\nimport { cn } from \"../lib/utils\";\nimport { formatMessageTime } from \"../utils/format-date\";\nimport {\n Check,\n CheckCheck,\n AlertCircle,\n Clock,\n Video,\n File,\n MapPin,\n Headphones,\n RotateCcw,\n MoreHorizontal,\n Trash2,\n Pencil,\n Ban,\n} from \"lucide-react\";\nimport {\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuTrigger,\n} from \"./ui/dropdown-menu\";\nimport {\n AlertDialog,\n AlertDialogAction,\n AlertDialogCancel,\n AlertDialogContent,\n AlertDialogDescription,\n AlertDialogFooter,\n AlertDialogHeader,\n AlertDialogTitle,\n} from \"./ui/alert-dialog\";\nimport { Button } from \"./ui/button\";\nimport { Textarea } from \"./ui/textarea\";\n\nconst statusIcons: Record<string, React.ReactNode> = {\n pending: <Clock className=\"h-3 w-3 animate-pulse\" />,\n sent: <Check className=\"h-3 w-3\" />,\n delivered: <CheckCheck className=\"h-3 w-3\" />,\n read: <CheckCheck className=\"h-3 w-3 text-blue-500\" />,\n failed: <AlertCircle className=\"h-3 w-3 text-destructive\" />,\n};\n\nfunction parseMetadata(metadata: string | null): Record<string, unknown> {\n if (!metadata) return {};\n try {\n return JSON.parse(metadata);\n } catch {\n return {};\n }\n}\n\nfunction MediaContent({ message }: { message: InboxMessage }) {\n switch (message.content_type) {\n case \"image\":\n return (\n <div className=\"space-y-1\">\n {message.content_url && (\n <img\n src={message.content_url}\n alt=\"Imagem\"\n className=\"max-w-[240px] rounded-md\"\n />\n )}\n {message.content && <p className=\"text-sm\">{message.content}</p>}\n </div>\n );\n case \"audio\":\n return (\n <div className=\"flex items-center gap-2\">\n <Headphones className=\"h-4 w-4\" />\n <span className=\"text-sm\">Mensagem de áudio</span>\n </div>\n );\n case \"video\":\n return (\n <div className=\"flex items-center gap-2\">\n <Video className=\"h-4 w-4\" />\n <span className=\"text-sm\">Vídeo</span>\n {message.content && <p className=\"text-sm\">{message.content}</p>}\n </div>\n );\n case \"document\": {\n const meta = parseMetadata(message.metadata);\n return (\n <div className=\"flex items-center gap-2\">\n <File className=\"h-4 w-4\" />\n <span className=\"text-sm\">\n {(meta.filename as string) || \"Documento\"}\n </span>\n </div>\n );\n }\n case \"location\":\n return (\n <div className=\"flex items-center gap-2\">\n <MapPin className=\"h-4 w-4\" />\n <span className=\"text-sm\">Localização</span>\n </div>\n );\n default:\n return null;\n }\n}\n\nexport interface MessageBubbleProps {\n message: InboxMessage;\n onRetry?: (message: InboxMessage) => void;\n onRevoke?: (message: InboxMessage) => void;\n onEdit?: (message: InboxMessage, newContent: string) => void;\n renderActions?: (message: InboxMessage) => React.ReactNode;\n}\n\nexport function MessageBubble({\n message,\n onRetry,\n onRevoke,\n onEdit,\n renderActions,\n}: MessageBubbleProps) {\n const [showRevokeDialog, setShowRevokeDialog] = useState(false);\n const [editing, setEditing] = useState(false);\n const [editContent, setEditContent] = useState(message.content || \"\");\n\n const isOutbound = message.direction === \"outbound\";\n const isPending = message.status === \"pending\";\n const isFailed = message.status === \"failed\";\n const time = formatMessageTime(message.datetime_add);\n\n const meta = parseMetadata(message.metadata);\n const isRevoked = meta.revoked === true;\n const isEdited = meta.edited === true;\n\n const agentLabel =\n isOutbound && message.source !== \"contact\"\n ? (meta.agent_name as string) ||\n (message.source === \"bot\" ? \"Bot\" : \"Agente\")\n : null;\n\n const canAct =\n isOutbound &&\n message.id > 0 &&\n !isPending &&\n !isFailed &&\n !isRevoked &&\n !!message.external_id;\n\n const canEdit = canAct && message.content_type === \"text\";\n\n function handleSaveEdit() {\n const trimmed = editContent.trim();\n if (!trimmed || trimmed === message.content) {\n setEditing(false);\n return;\n }\n onEdit?.(message, trimmed);\n setEditing(false);\n }\n\n if (isRevoked) {\n return (\n <div\n className={cn(\"flex\", isOutbound ? \"justify-end\" : \"justify-start\")}\n >\n <div className=\"max-w-[75%]\">\n <div\n className={cn(\n \"rounded-lg px-3 py-2\",\n isOutbound ? \"bg-primary/40\" : \"bg-muted/60\",\n )}\n >\n <div className=\"flex items-center gap-1.5\">\n <Ban\n className={cn(\n \"h-3.5 w-3.5\",\n isOutbound\n ? \"text-primary-foreground/50\"\n : \"text-muted-foreground\",\n )}\n />\n <p\n className={cn(\n \"text-sm italic\",\n isOutbound\n ? \"text-primary-foreground/50\"\n : \"text-muted-foreground\",\n )}\n >\n Mensagem apagada\n </p>\n </div>\n <div\n className={cn(\n \"mt-1 flex items-center justify-end gap-1\",\n isOutbound\n ? \"text-primary-foreground/40\"\n : \"text-muted-foreground/60\",\n )}\n >\n <span className=\"text-[10px]\">{time}</span>\n </div>\n </div>\n </div>\n </div>\n );\n }\n\n return (\n <div\n className={cn(\n \"flex group\",\n isOutbound ? \"justify-end\" : \"justify-start\",\n )}\n >\n <div className=\"max-w-[75%] relative\">\n {canAct && (\n <div className=\"absolute -top-1 right-1 opacity-0 group-hover:opacity-100 transition-opacity z-10\">\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <button className=\"h-6 w-6 rounded-full bg-background/80 shadow-sm flex items-center justify-center hover:bg-background\">\n <MoreHorizontal className=\"h-3.5 w-3.5 text-muted-foreground\" />\n </button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\" className=\"w-40\">\n {canEdit && (\n <DropdownMenuItem\n onSelect={(e) => {\n e.preventDefault();\n setEditing(true);\n setEditContent(message.content || \"\");\n }}\n >\n <Pencil className=\"h-4 w-4 mr-2\" />\n Editar\n </DropdownMenuItem>\n )}\n <DropdownMenuItem\n onSelect={(e) => {\n e.preventDefault();\n setShowRevokeDialog(true);\n }}\n className=\"text-destructive focus:text-destructive\"\n >\n <Trash2 className=\"h-4 w-4 mr-2\" />\n Apagar para todos\n </DropdownMenuItem>\n </DropdownMenuContent>\n </DropdownMenu>\n </div>\n )}\n\n {renderActions?.(message)}\n\n <div\n className={cn(\n \"rounded-lg px-3 py-2\",\n isOutbound ? \"bg-primary text-primary-foreground\" : \"bg-muted\",\n isPending && \"opacity-70\",\n isFailed && \"bg-destructive/10 border border-destructive/30\",\n )}\n >\n {agentLabel && (\n <p\n className={cn(\n \"mb-0.5 text-[10px] font-medium\",\n isFailed\n ? \"text-destructive/70\"\n : isOutbound\n ? \"text-primary-foreground/70\"\n : \"text-muted-foreground\",\n )}\n >\n {agentLabel}\n </p>\n )}\n\n {editing ? (\n <div className=\"space-y-2\">\n <Textarea\n value={editContent}\n onChange={(e) => setEditContent(e.target.value)}\n rows={2}\n className=\"min-h-[36px] max-h-[120px] resize-none bg-background text-foreground text-sm\"\n autoFocus\n onKeyDown={(e) => {\n if (e.key === \"Enter\" && !e.shiftKey) {\n e.preventDefault();\n handleSaveEdit();\n }\n if (e.key === \"Escape\") {\n setEditing(false);\n }\n }}\n />\n <div className=\"flex justify-end gap-1.5\">\n <Button\n size=\"sm\"\n variant=\"ghost\"\n className=\"h-6 text-xs px-2\"\n onClick={() => setEditing(false)}\n >\n Cancelar\n </Button>\n <Button\n size=\"sm\"\n className=\"h-6 text-xs px-2\"\n onClick={handleSaveEdit}\n disabled={\n !editContent.trim() ||\n editContent.trim() === message.content\n }\n >\n Salvar\n </Button>\n </div>\n </div>\n ) : message.content_type === \"text\" ? (\n <p\n className={cn(\n \"text-sm whitespace-pre-wrap break-words\",\n isFailed && \"text-foreground\",\n )}\n >\n {message.content}\n </p>\n ) : (\n <MediaContent message={message} />\n )}\n\n {!editing && (\n <div\n className={cn(\n \"mt-1 flex items-center justify-end gap-1\",\n isFailed\n ? \"text-destructive\"\n : isOutbound\n ? \"text-primary-foreground/60\"\n : \"text-muted-foreground\",\n )}\n >\n {isEdited && (\n <span className=\"text-[10px] italic\">editada</span>\n )}\n <span className=\"text-[10px]\">{time}</span>\n {isOutbound && statusIcons[message.status]}\n </div>\n )}\n </div>\n\n {isFailed && (\n <div className=\"mt-1 flex items-center gap-1.5 justify-end px-1\">\n <AlertCircle className=\"h-3 w-3 shrink-0 text-destructive\" />\n <span className=\"text-[11px] text-destructive\">\n {message._error || \"Erro ao enviar\"}\n </span>\n {onRetry && (\n <button\n onClick={() => onRetry(message)}\n className=\"inline-flex items-center gap-0.5 text-[11px] text-destructive font-medium hover:text-destructive/80 hover:underline cursor-pointer ml-1\"\n >\n <RotateCcw className=\"h-3 w-3\" />\n Reenviar\n </button>\n )}\n </div>\n )}\n </div>\n\n <AlertDialog open={showRevokeDialog} onOpenChange={setShowRevokeDialog}>\n <AlertDialogContent>\n <AlertDialogHeader>\n <AlertDialogTitle>Apagar mensagem?</AlertDialogTitle>\n <AlertDialogDescription>\n A mensagem será apagada para todos no WhatsApp. Ela continuará\n visível aqui como "mensagem apagada".\n </AlertDialogDescription>\n </AlertDialogHeader>\n <AlertDialogFooter>\n <AlertDialogCancel>Cancelar</AlertDialogCancel>\n <AlertDialogAction\n className=\"bg-destructive text-destructive-foreground hover:bg-destructive/90\"\n onClick={() => onRevoke?.(message)}\n >\n Apagar para todos\n </AlertDialogAction>\n </AlertDialogFooter>\n </AlertDialogContent>\n </AlertDialog>\n </div>\n );\n}\n","import * as React from \"react\";\nimport { DropdownMenu as DropdownMenuPrimitive } from \"radix-ui\";\nimport { Check, ChevronRight } from \"lucide-react\";\n\nimport { cn } from \"../../lib/utils\";\n\nfunction DropdownMenu({\n ...props\n}: React.ComponentProps<typeof DropdownMenuPrimitive.Root>) {\n return <DropdownMenuPrimitive.Root data-slot=\"dropdown-menu\" {...props} />;\n}\n\nfunction DropdownMenuTrigger({\n ...props\n}: React.ComponentProps<typeof DropdownMenuPrimitive.Trigger>) {\n return (\n <DropdownMenuPrimitive.Trigger\n data-slot=\"dropdown-menu-trigger\"\n {...props}\n />\n );\n}\n\nfunction DropdownMenuContent({\n className,\n align = \"start\",\n sideOffset = 4,\n ...props\n}: React.ComponentProps<typeof DropdownMenuPrimitive.Content>) {\n return (\n <DropdownMenuPrimitive.Portal>\n <DropdownMenuPrimitive.Content\n data-slot=\"dropdown-menu-content\"\n sideOffset={sideOffset}\n align={align}\n className={cn(\n \"data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 data-closed:zoom-out-95 data-open:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 ring-foreground/10 bg-popover text-popover-foreground min-w-32 rounded-md p-1 shadow-md ring-1 duration-100 z-50 overflow-x-hidden overflow-y-auto\",\n className,\n )}\n {...props}\n />\n </DropdownMenuPrimitive.Portal>\n );\n}\n\nfunction DropdownMenuGroup({\n ...props\n}: React.ComponentProps<typeof DropdownMenuPrimitive.Group>) {\n return (\n <DropdownMenuPrimitive.Group data-slot=\"dropdown-menu-group\" {...props} />\n );\n}\n\nfunction DropdownMenuItem({\n className,\n inset,\n variant = \"default\",\n ...props\n}: React.ComponentProps<typeof DropdownMenuPrimitive.Item> & {\n inset?: boolean;\n variant?: \"default\" | \"destructive\";\n}) {\n return (\n <DropdownMenuPrimitive.Item\n data-slot=\"dropdown-menu-item\"\n data-inset={inset}\n data-variant={variant}\n className={cn(\n \"focus:bg-accent focus:text-accent-foreground data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 data-[variant=destructive]:focus:text-destructive gap-2 rounded-sm px-2 py-1.5 text-sm data-inset:pl-8 [&_svg:not([class*='size-'])]:size-4 relative flex cursor-default items-center outline-hidden select-none data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0\",\n className,\n )}\n {...props}\n />\n );\n}\n\nfunction DropdownMenuCheckboxItem({\n className,\n children,\n checked,\n ...props\n}: React.ComponentProps<typeof DropdownMenuPrimitive.CheckboxItem>) {\n return (\n <DropdownMenuPrimitive.CheckboxItem\n data-slot=\"dropdown-menu-checkbox-item\"\n className={cn(\n \"focus:bg-accent focus:text-accent-foreground gap-2 rounded-sm py-1.5 pr-8 pl-2 text-sm [&_svg:not([class*='size-'])]:size-4 relative flex cursor-default items-center outline-hidden select-none data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0\",\n className,\n )}\n checked={checked}\n {...props}\n >\n <span className=\"absolute right-2 flex items-center justify-center pointer-events-none\">\n <DropdownMenuPrimitive.ItemIndicator>\n <Check />\n </DropdownMenuPrimitive.ItemIndicator>\n </span>\n {children}\n </DropdownMenuPrimitive.CheckboxItem>\n );\n}\n\nfunction DropdownMenuSeparator({\n className,\n ...props\n}: React.ComponentProps<typeof DropdownMenuPrimitive.Separator>) {\n return (\n <DropdownMenuPrimitive.Separator\n data-slot=\"dropdown-menu-separator\"\n className={cn(\"bg-border -mx-1 my-1 h-px\", className)}\n {...props}\n />\n );\n}\n\nfunction DropdownMenuSub({\n ...props\n}: React.ComponentProps<typeof DropdownMenuPrimitive.Sub>) {\n return <DropdownMenuPrimitive.Sub data-slot=\"dropdown-menu-sub\" {...props} />;\n}\n\nfunction DropdownMenuSubTrigger({\n className,\n inset,\n children,\n ...props\n}: React.ComponentProps<typeof DropdownMenuPrimitive.SubTrigger> & {\n inset?: boolean;\n}) {\n return (\n <DropdownMenuPrimitive.SubTrigger\n data-slot=\"dropdown-menu-sub-trigger\"\n data-inset={inset}\n className={cn(\n \"focus:bg-accent focus:text-accent-foreground data-open:bg-accent gap-2 rounded-sm px-2 py-1.5 text-sm data-inset:pl-8 [&_svg:not([class*='size-'])]:size-4 flex cursor-default items-center outline-hidden select-none [&_svg]:pointer-events-none [&_svg]:shrink-0\",\n className,\n )}\n {...props}\n >\n {children}\n <ChevronRight className=\"ml-auto\" />\n </DropdownMenuPrimitive.SubTrigger>\n );\n}\n\nfunction DropdownMenuSubContent({\n className,\n ...props\n}: React.ComponentProps<typeof DropdownMenuPrimitive.SubContent>) {\n return (\n <DropdownMenuPrimitive.SubContent\n data-slot=\"dropdown-menu-sub-content\"\n className={cn(\n \"data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 data-closed:zoom-out-95 data-open:zoom-in-95 ring-foreground/10 bg-popover text-popover-foreground min-w-[96px] rounded-md p-1 shadow-lg ring-1 duration-100 z-50 overflow-hidden\",\n className,\n )}\n {...props}\n />\n );\n}\n\nexport {\n DropdownMenu,\n DropdownMenuTrigger,\n DropdownMenuContent,\n DropdownMenuGroup,\n DropdownMenuItem,\n DropdownMenuCheckboxItem,\n DropdownMenuSeparator,\n DropdownMenuSub,\n DropdownMenuSubTrigger,\n DropdownMenuSubContent,\n};\n","import * as React from \"react\";\nimport { AlertDialog as AlertDialogPrimitive } from \"radix-ui\";\n\nimport { cn } from \"../../lib/utils\";\nimport { Button } from \"./button\";\n\nfunction AlertDialog({\n ...props\n}: React.ComponentProps<typeof AlertDialogPrimitive.Root>) {\n return <AlertDialogPrimitive.Root data-slot=\"alert-dialog\" {...props} />;\n}\n\nfunction AlertDialogTrigger({\n ...props\n}: React.ComponentProps<typeof AlertDialogPrimitive.Trigger>) {\n return (\n <AlertDialogPrimitive.Trigger data-slot=\"alert-dialog-trigger\" {...props} />\n );\n}\n\nfunction AlertDialogPortal({\n ...props\n}: React.ComponentProps<typeof AlertDialogPrimitive.Portal>) {\n return (\n <AlertDialogPrimitive.Portal data-slot=\"alert-dialog-portal\" {...props} />\n );\n}\n\nfunction AlertDialogOverlay({\n className,\n ...props\n}: React.ComponentProps<typeof AlertDialogPrimitive.Overlay>) {\n return (\n <AlertDialogPrimitive.Overlay\n data-slot=\"alert-dialog-overlay\"\n className={cn(\n \"data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 bg-black/10 duration-100 supports-backdrop-filter:backdrop-blur-xs fixed inset-0 z-50\",\n className,\n )}\n {...props}\n />\n );\n}\n\nfunction AlertDialogContent({\n className,\n ...props\n}: React.ComponentProps<typeof AlertDialogPrimitive.Content>) {\n return (\n <AlertDialogPortal>\n <AlertDialogOverlay />\n <AlertDialogPrimitive.Content\n data-slot=\"alert-dialog-content\"\n className={cn(\n \"data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 data-closed:zoom-out-95 data-open:zoom-in-95 bg-background ring-foreground/10 gap-6 rounded-xl p-6 ring-1 duration-100 sm:max-w-lg fixed top-1/2 left-1/2 z-50 grid w-full max-w-xs -translate-x-1/2 -translate-y-1/2 outline-none\",\n className,\n )}\n {...props}\n />\n </AlertDialogPortal>\n );\n}\n\nfunction AlertDialogHeader({\n className,\n ...props\n}: React.ComponentProps<\"div\">) {\n return (\n <div\n data-slot=\"alert-dialog-header\"\n className={cn(\"grid gap-1.5 text-center sm:text-left\", className)}\n {...props}\n />\n );\n}\n\nfunction AlertDialogFooter({\n className,\n ...props\n}: React.ComponentProps<\"div\">) {\n return (\n <div\n data-slot=\"alert-dialog-footer\"\n className={cn(\n \"flex flex-col-reverse gap-2 sm:flex-row sm:justify-end\",\n className,\n )}\n {...props}\n />\n );\n}\n\nfunction AlertDialogTitle({\n className,\n ...props\n}: React.ComponentProps<typeof AlertDialogPrimitive.Title>) {\n return (\n <AlertDialogPrimitive.Title\n data-slot=\"alert-dialog-title\"\n className={cn(\"text-lg font-medium\", className)}\n {...props}\n />\n );\n}\n\nfunction AlertDialogDescription({\n className,\n ...props\n}: React.ComponentProps<typeof AlertDialogPrimitive.Description>) {\n return (\n <AlertDialogPrimitive.Description\n data-slot=\"alert-dialog-description\"\n className={cn(\"text-muted-foreground text-sm\", className)}\n {...props}\n />\n );\n}\n\nfunction AlertDialogAction({\n className,\n variant = \"default\",\n size = \"default\",\n ...props\n}: React.ComponentProps<typeof AlertDialogPrimitive.Action> &\n Partial<Pick<React.ComponentProps<typeof Button>, \"variant\" | \"size\">>) {\n return (\n <Button variant={variant} size={size} asChild>\n <AlertDialogPrimitive.Action\n data-slot=\"alert-dialog-action\"\n className={cn(className)}\n {...props}\n />\n </Button>\n );\n}\n\nfunction AlertDialogCancel({\n className,\n variant = \"outline\",\n size = \"default\",\n ...props\n}: React.ComponentProps<typeof AlertDialogPrimitive.Cancel> &\n Partial<Pick<React.ComponentProps<typeof Button>, \"variant\" | \"size\">>) {\n return (\n <Button variant={variant} size={size} asChild>\n <AlertDialogPrimitive.Cancel\n data-slot=\"alert-dialog-cancel\"\n className={cn(className)}\n {...props}\n />\n </Button>\n );\n}\n\nexport {\n AlertDialog,\n AlertDialogAction,\n AlertDialogCancel,\n AlertDialogContent,\n AlertDialogDescription,\n AlertDialogFooter,\n AlertDialogHeader,\n AlertDialogOverlay,\n AlertDialogPortal,\n AlertDialogTitle,\n AlertDialogTrigger,\n};\n","import * as React from \"react\";\nimport { cva, type VariantProps } from \"class-variance-authority\";\nimport { Slot } from \"radix-ui\";\n\nimport { cn } from \"../../lib/utils\";\n\nconst buttonVariants = cva(\n \"focus-visible:border-ring focus-visible:ring-ring/50 rounded-md border border-transparent text-sm font-medium focus-visible:ring-3 [&_svg:not([class*='size-'])]:size-4 inline-flex items-center justify-center whitespace-nowrap transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none shrink-0 [&_svg]:shrink-0 outline-none select-none\",\n {\n variants: {\n variant: {\n default: \"bg-primary text-primary-foreground hover:bg-primary/80\",\n outline:\n \"border-border bg-background hover:bg-muted hover:text-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50 shadow-xs\",\n secondary:\n \"bg-secondary text-secondary-foreground hover:bg-secondary/80\",\n ghost:\n \"hover:bg-muted hover:text-foreground dark:hover:bg-muted/50\",\n destructive:\n \"bg-destructive/10 hover:bg-destructive/20 focus-visible:ring-destructive/20 dark:bg-destructive/20 text-destructive\",\n link: \"text-primary underline-offset-4 hover:underline\",\n },\n size: {\n default: \"h-9 gap-1.5 px-2.5\",\n xs: \"h-6 gap-1 px-2 text-xs [&_svg:not([class*='size-'])]:size-3\",\n sm: \"h-8 gap-1 px-2.5\",\n lg: \"h-10 gap-1.5 px-2.5\",\n icon: \"size-9\",\n \"icon-xs\": \"size-6 [&_svg:not([class*='size-'])]:size-3\",\n \"icon-sm\": \"size-8\",\n \"icon-lg\": \"size-10\",\n },\n },\n defaultVariants: {\n variant: \"default\",\n size: \"default\",\n },\n },\n);\n\nfunction Button({\n className,\n variant = \"default\",\n size = \"default\",\n asChild = false,\n ...props\n}: React.ComponentProps<\"button\"> &\n VariantProps<typeof buttonVariants> & {\n asChild?: boolean;\n }) {\n const Comp = asChild ? Slot.Root : \"button\";\n\n return (\n <Comp\n data-slot=\"button\"\n className={cn(buttonVariants({ variant, size, className }))}\n {...props}\n />\n );\n}\n\nexport { Button, buttonVariants };\n","import * as React from \"react\";\n\nimport { cn } from \"../../lib/utils\";\n\nfunction Textarea({ className, ...props }: React.ComponentProps<\"textarea\">) {\n return (\n <textarea\n data-slot=\"textarea\"\n className={cn(\n \"border-input dark:bg-input/30 focus-visible:border-ring focus-visible:ring-ring/50 rounded-md border bg-transparent px-2.5 py-2 text-base shadow-xs transition-[color,box-shadow] focus-visible:ring-3 md:text-sm placeholder:text-muted-foreground flex field-sizing-content min-h-16 w-full outline-none disabled:cursor-not-allowed disabled:opacity-50\",\n className,\n )}\n {...props}\n />\n );\n}\n\nexport { Textarea };\n","import { useState, useRef } from \"react\";\nimport { Send } from \"lucide-react\";\nimport { Button } from \"./ui/button\";\nimport { Textarea } from \"./ui/textarea\";\n\nexport interface ChatInputProps {\n onSend: (content: string) => void;\n disabled?: boolean;\n}\n\nexport function ChatInput({ onSend, disabled }: ChatInputProps) {\n const [content, setContent] = useState(\"\");\n const textareaRef = useRef<HTMLTextAreaElement>(null);\n\n function handleSend() {\n const text = content.trim();\n if (!text || disabled) return;\n\n setContent(\"\");\n onSend(text);\n textareaRef.current?.focus();\n }\n\n function handleKeyDown(e: React.KeyboardEvent) {\n if (e.key === \"Enter\" && !e.shiftKey) {\n e.preventDefault();\n handleSend();\n }\n }\n\n return (\n <div className=\"border-t p-3\">\n <div className=\"flex items-end gap-2\">\n <Textarea\n ref={textareaRef}\n placeholder=\"Digite uma mensagem...\"\n value={content}\n onChange={(e) => setContent(e.target.value)}\n onKeyDown={handleKeyDown}\n rows={1}\n className=\"min-h-[40px] max-h-[120px] resize-none\"\n />\n <Button\n size=\"icon\"\n onClick={handleSend}\n disabled={!content.trim() || disabled}\n className=\"shrink-0\"\n >\n <Send className=\"h-4 w-4\" />\n </Button>\n </div>\n </div>\n );\n}\n","import { cn } from \"../../lib/utils\";\n\nfunction Skeleton({\n className,\n ...props\n}: React.ComponentProps<\"div\">) {\n return (\n <div\n data-slot=\"skeleton\"\n className={cn(\"bg-muted rounded-md animate-pulse\", className)}\n {...props}\n />\n );\n}\n\nexport { Skeleton };\n","import * as React from \"react\";\nimport { Select as SelectPrimitive } from \"radix-ui\";\nimport { Check, ChevronDown, ChevronUp, ChevronsUpDown } from \"lucide-react\";\n\nimport { cn } from \"../../lib/utils\";\n\nfunction Select({\n ...props\n}: React.ComponentProps<typeof SelectPrimitive.Root>) {\n return <SelectPrimitive.Root data-slot=\"select\" {...props} />;\n}\n\nfunction SelectGroup({\n className,\n ...props\n}: React.ComponentProps<typeof SelectPrimitive.Group>) {\n return (\n <SelectPrimitive.Group\n data-slot=\"select-group\"\n className={cn(\"scroll-my-1 p-1\", className)}\n {...props}\n />\n );\n}\n\nfunction SelectValue({\n ...props\n}: React.ComponentProps<typeof SelectPrimitive.Value>) {\n return <SelectPrimitive.Value data-slot=\"select-value\" {...props} />;\n}\n\nfunction SelectTrigger({\n className,\n size = \"default\",\n children,\n ...props\n}: React.ComponentProps<typeof SelectPrimitive.Trigger> & {\n size?: \"sm\" | \"default\";\n}) {\n return (\n <SelectPrimitive.Trigger\n data-slot=\"select-trigger\"\n data-size={size}\n className={cn(\n \"border-input data-placeholder:text-muted-foreground dark:bg-input/30 dark:hover:bg-input/50 focus-visible:border-ring focus-visible:ring-ring/50 gap-1.5 rounded-md border bg-transparent py-2 pr-2 pl-2.5 text-sm shadow-xs transition-[color,box-shadow] focus-visible:ring-3 data-[size=default]:h-9 data-[size=sm]:h-8 [&_svg:not([class*='size-'])]:size-4 flex w-fit items-center justify-between whitespace-nowrap outline-none disabled:cursor-not-allowed disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0\",\n className,\n )}\n {...props}\n >\n {children}\n <SelectPrimitive.Icon asChild>\n <ChevronsUpDown className=\"text-muted-foreground size-4 pointer-events-none\" />\n </SelectPrimitive.Icon>\n </SelectPrimitive.Trigger>\n );\n}\n\nfunction SelectContent({\n className,\n children,\n position = \"item-aligned\",\n align = \"center\",\n ...props\n}: React.ComponentProps<typeof SelectPrimitive.Content>) {\n return (\n <SelectPrimitive.Portal>\n <SelectPrimitive.Content\n data-slot=\"select-content\"\n className={cn(\n \"bg-popover text-popover-foreground data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 data-closed:zoom-out-95 data-open:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 ring-foreground/10 min-w-36 rounded-md shadow-md ring-1 duration-100 relative z-50 max-h-(--radix-select-content-available-height) overflow-x-hidden overflow-y-auto\",\n position === \"popper\" &&\n \"data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1\",\n className,\n )}\n position={position}\n align={align}\n {...props}\n >\n <SelectScrollUpButton />\n <SelectPrimitive.Viewport>{children}</SelectPrimitive.Viewport>\n <SelectScrollDownButton />\n </SelectPrimitive.Content>\n </SelectPrimitive.Portal>\n );\n}\n\nfunction SelectItem({\n className,\n children,\n ...props\n}: React.ComponentProps<typeof SelectPrimitive.Item>) {\n return (\n <SelectPrimitive.Item\n data-slot=\"select-item\"\n className={cn(\n \"focus:bg-accent focus:text-accent-foreground gap-2 rounded-sm py-1.5 pr-8 pl-2 text-sm [&_svg:not([class*='size-'])]:size-4 relative flex w-full cursor-default items-center outline-hidden select-none data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0\",\n className,\n )}\n {...props}\n >\n <span className=\"pointer-events-none absolute right-2 flex size-4 items-center justify-center\">\n <SelectPrimitive.ItemIndicator>\n <Check className=\"pointer-events-none\" />\n </SelectPrimitive.ItemIndicator>\n </span>\n <SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>\n </SelectPrimitive.Item>\n );\n}\n\nfunction SelectScrollUpButton({\n className,\n ...props\n}: React.ComponentProps<typeof SelectPrimitive.ScrollUpButton>) {\n return (\n <SelectPrimitive.ScrollUpButton\n data-slot=\"select-scroll-up-button\"\n className={cn(\n \"bg-popover z-10 flex cursor-default items-center justify-center py-1 [&_svg:not([class*='size-'])]:size-4\",\n className,\n )}\n {...props}\n >\n <ChevronUp />\n </SelectPrimitive.ScrollUpButton>\n );\n}\n\nfunction SelectScrollDownButton({\n className,\n ...props\n}: React.ComponentProps<typeof SelectPrimitive.ScrollDownButton>) {\n return (\n <SelectPrimitive.ScrollDownButton\n data-slot=\"select-scroll-down-button\"\n className={cn(\n \"bg-popover z-10 flex cursor-default items-center justify-center py-1 [&_svg:not([class*='size-'])]:size-4\",\n className,\n )}\n {...props}\n >\n <ChevronDown />\n </SelectPrimitive.ScrollDownButton>\n );\n}\n\nexport {\n Select,\n SelectContent,\n SelectGroup,\n SelectItem,\n SelectScrollDownButton,\n SelectScrollUpButton,\n SelectTrigger,\n SelectValue,\n};\n"],"mappings":"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","names":["useRef","Check","jsx","jsx","jsxs","jsx","jsxs","jsx","jsx","jsxs","Check","useState","jsx","jsxs","useState","jsx","Check","jsx","jsxs","jsx","jsxs","Check","jsx","jsxs","useRef"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@greatapps/greatchat-ui",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -24,7 +24,9 @@
|
|
|
24
24
|
"sonner": "*"
|
|
25
25
|
},
|
|
26
26
|
"dependencies": {
|
|
27
|
+
"class-variance-authority": "^0.7.1",
|
|
27
28
|
"clsx": "^2",
|
|
29
|
+
"radix-ui": "^1.4.3",
|
|
28
30
|
"tailwind-merge": "^3"
|
|
29
31
|
},
|
|
30
32
|
"devDependencies": {
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { useState, useRef } from "react";
|
|
2
|
+
import { Send } from "lucide-react";
|
|
3
|
+
import { Button } from "./ui/button";
|
|
4
|
+
import { Textarea } from "./ui/textarea";
|
|
5
|
+
|
|
6
|
+
export interface ChatInputProps {
|
|
7
|
+
onSend: (content: string) => void;
|
|
8
|
+
disabled?: boolean;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function ChatInput({ onSend, disabled }: ChatInputProps) {
|
|
12
|
+
const [content, setContent] = useState("");
|
|
13
|
+
const textareaRef = useRef<HTMLTextAreaElement>(null);
|
|
14
|
+
|
|
15
|
+
function handleSend() {
|
|
16
|
+
const text = content.trim();
|
|
17
|
+
if (!text || disabled) return;
|
|
18
|
+
|
|
19
|
+
setContent("");
|
|
20
|
+
onSend(text);
|
|
21
|
+
textareaRef.current?.focus();
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function handleKeyDown(e: React.KeyboardEvent) {
|
|
25
|
+
if (e.key === "Enter" && !e.shiftKey) {
|
|
26
|
+
e.preventDefault();
|
|
27
|
+
handleSend();
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return (
|
|
32
|
+
<div className="border-t p-3">
|
|
33
|
+
<div className="flex items-end gap-2">
|
|
34
|
+
<Textarea
|
|
35
|
+
ref={textareaRef}
|
|
36
|
+
placeholder="Digite uma mensagem..."
|
|
37
|
+
value={content}
|
|
38
|
+
onChange={(e) => setContent(e.target.value)}
|
|
39
|
+
onKeyDown={handleKeyDown}
|
|
40
|
+
rows={1}
|
|
41
|
+
className="min-h-[40px] max-h-[120px] resize-none"
|
|
42
|
+
/>
|
|
43
|
+
<Button
|
|
44
|
+
size="icon"
|
|
45
|
+
onClick={handleSend}
|
|
46
|
+
disabled={!content.trim() || disabled}
|
|
47
|
+
className="shrink-0"
|
|
48
|
+
>
|
|
49
|
+
<Send className="h-4 w-4" />
|
|
50
|
+
</Button>
|
|
51
|
+
</div>
|
|
52
|
+
</div>
|
|
53
|
+
);
|
|
54
|
+
}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { useRef, useEffect } from "react";
|
|
2
|
+
import type { InboxMessage } from "../types";
|
|
3
|
+
import { groupMessagesByDate } from "../utils/group-messages";
|
|
4
|
+
import { formatDateGroup } from "../utils/format-date";
|
|
5
|
+
import { MessageBubble } from "./message-bubble";
|
|
6
|
+
import { ChatInput } from "./chat-input";
|
|
7
|
+
import { Skeleton } from "./ui/skeleton";
|
|
8
|
+
import {
|
|
9
|
+
Select,
|
|
10
|
+
SelectContent,
|
|
11
|
+
SelectItem,
|
|
12
|
+
SelectTrigger,
|
|
13
|
+
SelectValue,
|
|
14
|
+
} from "./ui/select";
|
|
15
|
+
|
|
16
|
+
export interface ChatViewProps {
|
|
17
|
+
messages: InboxMessage[];
|
|
18
|
+
isLoading: boolean;
|
|
19
|
+
inboxStatus?: string;
|
|
20
|
+
onStatusChange?: (status: string) => void;
|
|
21
|
+
onSend: (content: string) => void;
|
|
22
|
+
onRetry?: (message: InboxMessage) => void;
|
|
23
|
+
onRevoke?: (message: InboxMessage) => void;
|
|
24
|
+
onEdit?: (message: InboxMessage, newContent: string) => void;
|
|
25
|
+
sendDisabled?: boolean;
|
|
26
|
+
renderHeader?: React.ReactNode;
|
|
27
|
+
renderStatusDropdown?: React.ReactNode;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function ChatView({
|
|
31
|
+
messages,
|
|
32
|
+
isLoading,
|
|
33
|
+
inboxStatus,
|
|
34
|
+
onStatusChange,
|
|
35
|
+
onSend,
|
|
36
|
+
onRetry,
|
|
37
|
+
onRevoke,
|
|
38
|
+
onEdit,
|
|
39
|
+
sendDisabled,
|
|
40
|
+
renderHeader,
|
|
41
|
+
renderStatusDropdown,
|
|
42
|
+
}: ChatViewProps) {
|
|
43
|
+
const scrollRef = useRef<HTMLDivElement>(null);
|
|
44
|
+
const prevMessageCount = useRef(0);
|
|
45
|
+
|
|
46
|
+
useEffect(() => {
|
|
47
|
+
if (messages && messages.length > prevMessageCount.current) {
|
|
48
|
+
const el = scrollRef.current;
|
|
49
|
+
if (el) {
|
|
50
|
+
setTimeout(() => {
|
|
51
|
+
el.scrollTop = el.scrollHeight;
|
|
52
|
+
}, 50);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
prevMessageCount.current = messages?.length || 0;
|
|
56
|
+
}, [messages]);
|
|
57
|
+
|
|
58
|
+
const groups = messages ? groupMessagesByDate(messages) : [];
|
|
59
|
+
|
|
60
|
+
return (
|
|
61
|
+
<div className="flex h-full flex-col">
|
|
62
|
+
{/* Header */}
|
|
63
|
+
{renderHeader !== undefined ? (
|
|
64
|
+
renderHeader
|
|
65
|
+
) : (
|
|
66
|
+
<div className="flex items-center justify-between border-b px-4 py-3">
|
|
67
|
+
<div className="flex-1" />
|
|
68
|
+
{renderStatusDropdown !== undefined ? (
|
|
69
|
+
renderStatusDropdown
|
|
70
|
+
) : onStatusChange ? (
|
|
71
|
+
<Select
|
|
72
|
+
value={inboxStatus}
|
|
73
|
+
onValueChange={onStatusChange}
|
|
74
|
+
>
|
|
75
|
+
<SelectTrigger className="w-[130px] h-8 text-xs">
|
|
76
|
+
<SelectValue placeholder="Status" />
|
|
77
|
+
</SelectTrigger>
|
|
78
|
+
<SelectContent>
|
|
79
|
+
<SelectItem value="open">Aberta</SelectItem>
|
|
80
|
+
<SelectItem value="pending">Pendente</SelectItem>
|
|
81
|
+
<SelectItem value="resolved">Resolvida</SelectItem>
|
|
82
|
+
</SelectContent>
|
|
83
|
+
</Select>
|
|
84
|
+
) : null}
|
|
85
|
+
</div>
|
|
86
|
+
)}
|
|
87
|
+
|
|
88
|
+
{/* Messages */}
|
|
89
|
+
<div ref={scrollRef} className="flex-1 overflow-y-auto p-4">
|
|
90
|
+
{isLoading ? (
|
|
91
|
+
<div className="space-y-4">
|
|
92
|
+
{Array.from({ length: 4 }).map((_, i) => (
|
|
93
|
+
<div
|
|
94
|
+
key={i}
|
|
95
|
+
className={`flex ${i % 2 === 0 ? "justify-start" : "justify-end"}`}
|
|
96
|
+
>
|
|
97
|
+
<Skeleton className="h-12 w-64 rounded-lg" />
|
|
98
|
+
</div>
|
|
99
|
+
))}
|
|
100
|
+
</div>
|
|
101
|
+
) : groups.length === 0 ? (
|
|
102
|
+
<div className="flex h-full items-center justify-center text-sm text-muted-foreground">
|
|
103
|
+
Nenhuma mensagem ainda
|
|
104
|
+
</div>
|
|
105
|
+
) : (
|
|
106
|
+
<div className="space-y-4">
|
|
107
|
+
{groups.map((group, gi) => (
|
|
108
|
+
<div key={gi}>
|
|
109
|
+
<div className="mb-3 flex justify-center">
|
|
110
|
+
<span className="rounded-full bg-muted px-3 py-1 text-[11px] text-muted-foreground">
|
|
111
|
+
{formatDateGroup(group.date)}
|
|
112
|
+
</span>
|
|
113
|
+
</div>
|
|
114
|
+
<div className="space-y-1">
|
|
115
|
+
{group.messages.map((msg) => (
|
|
116
|
+
<MessageBubble
|
|
117
|
+
key={msg.id}
|
|
118
|
+
message={msg}
|
|
119
|
+
onRetry={onRetry}
|
|
120
|
+
onRevoke={onRevoke}
|
|
121
|
+
onEdit={onEdit}
|
|
122
|
+
/>
|
|
123
|
+
))}
|
|
124
|
+
</div>
|
|
125
|
+
</div>
|
|
126
|
+
))}
|
|
127
|
+
</div>
|
|
128
|
+
)}
|
|
129
|
+
</div>
|
|
130
|
+
|
|
131
|
+
{/* Input */}
|
|
132
|
+
<ChatInput onSend={onSend} disabled={sendDisabled} />
|
|
133
|
+
</div>
|
|
134
|
+
);
|
|
135
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export { ChatView } from "./chat-view";
|
|
2
|
+
export type { ChatViewProps } from "./chat-view";
|
|
3
|
+
|
|
4
|
+
export { ChatInput } from "./chat-input";
|
|
5
|
+
export type { ChatInputProps } from "./chat-input";
|
|
6
|
+
|
|
7
|
+
export { MessageBubble } from "./message-bubble";
|
|
8
|
+
export type { MessageBubbleProps } from "./message-bubble";
|