myoperator-ui 0.0.222 → 0.0.223-beta.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.
Files changed (2) hide show
  1. package/dist/index.js +319 -15
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -16577,6 +16577,306 @@ export interface ChatBubbleProps extends React.HTMLAttributes<HTMLDivElement> {
16577
16577
  name: "index.ts",
16578
16578
  content: prefixTailwindClasses(`export { ChatBubble } from "./chat-bubble";
16579
16579
  export type { ChatBubbleProps, ChatBubbleReply, DeliveryStatus } from "./types";
16580
+ `, prefix)
16581
+ }
16582
+ ]
16583
+ },
16584
+ "chat-list-item": {
16585
+ name: "chat-list-item",
16586
+ description: "A chat list item showing contact name, message preview, timestamp, delivery status, and channel badge",
16587
+ category: "custom",
16588
+ dependencies: [
16589
+ "clsx",
16590
+ "tailwind-merge",
16591
+ "lucide-react"
16592
+ ],
16593
+ internalDependencies: [],
16594
+ isMultiFile: true,
16595
+ directory: "chat-list-item",
16596
+ mainFile: "chat-list-item.tsx",
16597
+ files: [
16598
+ {
16599
+ name: "chat-list-item.tsx",
16600
+ content: prefixTailwindClasses(`import * as React from "react";
16601
+ import { cn } from "../../../lib/utils";
16602
+ import {
16603
+ Bot,
16604
+ Check,
16605
+ CheckCheck,
16606
+ Clock,
16607
+ FileText,
16608
+ Image as ImageIcon,
16609
+ } from "lucide-react";
16610
+
16611
+ /* \u2500\u2500 Types \u2500\u2500 */
16612
+
16613
+ export type MessageStatus = "sent" | "delivered" | "read";
16614
+
16615
+ export type MessageType = "text" | "document" | "image";
16616
+
16617
+ export interface ChatListItemProps
16618
+ extends Omit<React.HTMLAttributes<HTMLDivElement>, "onClick"> {
16619
+ /** Contact or customer name (supports ReactNode for highlighted search matches) */
16620
+ name: React.ReactNode;
16621
+ /** Last message preview text (supports ReactNode for highlighted search matches) */
16622
+ message: React.ReactNode;
16623
+ /** Timestamp display string (e.g. "2:30 PM", "Yesterday") */
16624
+ timestamp: string;
16625
+ /**
16626
+ * Delivery status of the last outbound message.
16627
+ * Mutually exclusive with \`unreadCount\` \u2014 when set, no unread badge is shown.
16628
+ * - \`sent\`: single gray checkmark (message left the server)
16629
+ * - \`delivered\`: double gray checkmarks (reached the customer's device)
16630
+ * - \`read\`: double blue checkmarks (customer opened the message)
16631
+ */
16632
+ messageStatus?: MessageStatus;
16633
+ /**
16634
+ * Number of unread messages from the customer.
16635
+ * Only shown when \`messageStatus\` is not set (i.e., last message was inbound).
16636
+ */
16637
+ unreadCount?: number;
16638
+ /**
16639
+ * SLA timer label showing how long the customer has been waiting (e.g. "2h", "50m").
16640
+ * Displayed as a warning-colored tag next to the name.
16641
+ * Typically appears on unread/inbound conversations.
16642
+ */
16643
+ slaTimer?: string;
16644
+ /**
16645
+ * Type of the last message \u2014 controls the icon prefix before the message text.
16646
+ * - \`text\`: no icon (default)
16647
+ * - \`document\`: file icon
16648
+ * - \`image\`: image icon
16649
+ */
16650
+ messageType?: MessageType;
16651
+ /** Channel identifier (e.g. "MY01") */
16652
+ channel: string;
16653
+ /** Name of the assigned agent */
16654
+ agentName?: string;
16655
+ /** Whether the assigned agent's account has been deleted \u2014 renders in error color */
16656
+ isAgentDeleted?: boolean;
16657
+ /** Whether the conversation is handled by an AI/IVR bot \u2014 shows bot icon */
16658
+ isBot?: boolean;
16659
+ /** Whether this item is currently selected/active in the inbox */
16660
+ isSelected?: boolean;
16661
+ /** Callback when the chat item is clicked */
16662
+ onClick?: (event: React.MouseEvent<HTMLDivElement>) => void;
16663
+ }
16664
+
16665
+ /* \u2500\u2500 Sub-components \u2500\u2500 */
16666
+
16667
+ function StatusIndicator({ status }: { status: MessageStatus }) {
16668
+ if (status === "sent") {
16669
+ return (
16670
+ <span aria-label="Sent">
16671
+ <Check className="size-4 text-semantic-text-placeholder shrink-0" aria-hidden="true" />
16672
+ </span>
16673
+ );
16674
+ }
16675
+ if (status === "delivered") {
16676
+ return (
16677
+ <span aria-label="Delivered">
16678
+ <CheckCheck className="size-4 text-semantic-text-placeholder shrink-0" aria-hidden="true" />
16679
+ </span>
16680
+ );
16681
+ }
16682
+ // read
16683
+ return (
16684
+ <span aria-label="Read">
16685
+ <CheckCheck className="size-4 text-[#47b5bc] shrink-0" aria-hidden="true" />
16686
+ </span>
16687
+ );
16688
+ }
16689
+
16690
+ function UnreadBadge({ count }: { count: number }) {
16691
+ return (
16692
+ <span
16693
+ className="shrink-0 inline-flex items-center justify-center rounded-full bg-semantic-border-accent font-semibold text-white"
16694
+ style={{ width: 18, height: 18, fontSize: 10, lineHeight: 1 }}
16695
+ aria-label={\`\${count > 99 ? "99+" : count} unread messages\`}
16696
+ >
16697
+ {count > 99 ? "99+" : count}
16698
+ </span>
16699
+ );
16700
+ }
16701
+
16702
+ function SlaTag({ timer }: { timer: string }) {
16703
+ return (
16704
+ <span
16705
+ className="flex items-center gap-2 h-5 px-[6px] py-[2px] rounded bg-semantic-warning-surface shrink-0"
16706
+ aria-label={\`SLA timer: \${timer}\`}
16707
+ >
16708
+ <Clock className="size-3 text-semantic-warning-text" aria-hidden="true" />
16709
+ <span className="text-[12px] text-semantic-warning-text">{timer}</span>
16710
+ </span>
16711
+ );
16712
+ }
16713
+
16714
+ function MessageTypeIcon({ type }: { type: MessageType }) {
16715
+ if (type === "document") {
16716
+ return <FileText className="size-[14px] text-semantic-text-placeholder shrink-0" />;
16717
+ }
16718
+ if (type === "image") {
16719
+ return <ImageIcon className="size-[14px] text-semantic-text-placeholder shrink-0" />;
16720
+ }
16721
+ return null;
16722
+ }
16723
+
16724
+ function ChannelPill({
16725
+ channel,
16726
+ agentName,
16727
+ isAgentDeleted,
16728
+ isBot,
16729
+ }: {
16730
+ channel: string;
16731
+ agentName?: string;
16732
+ isAgentDeleted?: boolean;
16733
+ isBot?: boolean;
16734
+ }) {
16735
+ const textColor = isAgentDeleted ? "text-semantic-error-text" : "text-semantic-text-primary";
16736
+
16737
+ return (
16738
+ <div className="flex items-center gap-3">
16739
+ <span
16740
+ className={cn(
16741
+ "inline-flex items-center gap-[6px] px-2 py-1 rounded-[12px] border border-solid border-semantic-border-layout text-[12px]",
16742
+ textColor
16743
+ )}
16744
+ >
16745
+ {channel}
16746
+ {agentName && (
16747
+ <>
16748
+ <span>-</span>
16749
+ <span className="truncate">{agentName}</span>
16750
+ </>
16751
+ )}
16752
+ {isBot && <Bot className="size-[14px] text-semantic-text-primary" aria-hidden="true" />}
16753
+ </span>
16754
+ </div>
16755
+ );
16756
+ }
16757
+
16758
+ /* \u2500\u2500 Main Component \u2500\u2500 */
16759
+
16760
+ /**
16761
+ * ChatListItem displays a conversation preview in an inbox-style list.
16762
+ *
16763
+ * Each item shows the contact name, last message preview, timestamp,
16764
+ * delivery status or unread count, optional SLA timer, and channel/agent info.
16765
+ *
16766
+ * @example
16767
+ * \`\`\`tsx
16768
+ * <ChatListItem
16769
+ * name="Aditi Kumar"
16770
+ * message="Have a look at this document"
16771
+ * timestamp="2:30 PM"
16772
+ * messageStatus="sent"
16773
+ * messageType="document"
16774
+ * channel="MY01"
16775
+ * agentName="Alex Smith"
16776
+ * onClick={() => setSelectedChat("1")}
16777
+ * />
16778
+ * \`\`\`
16779
+ */
16780
+ const ChatListItem = React.forwardRef(
16781
+ (
16782
+ {
16783
+ name,
16784
+ message,
16785
+ timestamp,
16786
+ messageStatus,
16787
+ unreadCount,
16788
+ slaTimer,
16789
+ messageType = "text",
16790
+ channel,
16791
+ agentName,
16792
+ isAgentDeleted = false,
16793
+ isBot = false,
16794
+ isSelected = false,
16795
+ onClick,
16796
+ className,
16797
+ ...props
16798
+ }: ChatListItemProps,
16799
+ ref: React.Ref<HTMLDivElement>
16800
+ ) => {
16801
+ const nameText = typeof name === "string" ? name : "";
16802
+ const messageText = typeof message === "string" ? message : "";
16803
+ const defaultAriaLabel = \`\${nameText}. \${messageText}. \${timestamp}\${unreadCount ? \`. \${unreadCount} unread\` : ""}\${slaTimer ? \`. SLA: \${slaTimer}\` : ""}\${messageStatus ? \`. \${messageStatus}\` : ""}\`;
16804
+
16805
+ return (
16806
+ <div
16807
+ ref={ref}
16808
+ role="button"
16809
+ tabIndex={0}
16810
+ aria-selected={isSelected}
16811
+ aria-label={defaultAriaLabel}
16812
+ onClick={onClick}
16813
+ onKeyDown={(e) => {
16814
+ if (e.key === "Enter" || e.key === " ") {
16815
+ e.preventDefault();
16816
+ onClick?.(e as unknown as React.MouseEvent<HTMLDivElement>);
16817
+ }
16818
+ }}
16819
+ className={cn(
16820
+ "flex items-start px-4 py-5 w-full transition-colors cursor-pointer",
16821
+ isSelected
16822
+ ? "bg-[var(--color-neutral-50)] border-l-4 border-solid border-l-semantic-border-accent border-b border-b-semantic-border-layout"
16823
+ : "bg-white hover:bg-[var(--color-neutral-50)] border-b border-solid border-semantic-border-layout",
16824
+ className
16825
+ )}
16826
+ {...props}
16827
+ >
16828
+ <div className="flex flex-col gap-2 flex-1 min-w-0">
16829
+ {/* Row 1: Name + SLA Timer + Status/Unread Badge */}
16830
+ <div className="flex items-center gap-[6px]">
16831
+ <div className="flex items-center gap-3 flex-1 min-w-0">
16832
+ <span className="text-[14px] text-semantic-text-primary truncate">
16833
+ {name}
16834
+ </span>
16835
+ {slaTimer && <SlaTag timer={slaTimer} />}
16836
+ </div>
16837
+ {messageStatus ? (
16838
+ <StatusIndicator status={messageStatus} />
16839
+ ) : unreadCount ? (
16840
+ <UnreadBadge count={unreadCount} />
16841
+ ) : null}
16842
+ </div>
16843
+
16844
+ {/* Row 2: Message Type Icon + Message Preview + Timestamp */}
16845
+ <div className="flex items-center gap-[6px]">
16846
+ <MessageTypeIcon type={messageType} />
16847
+ <p className="flex-1 text-[14px] text-semantic-text-muted truncate min-w-0 m-0">
16848
+ {message}
16849
+ </p>
16850
+ <span className="text-[12px] text-semantic-text-placeholder tracking-[0.06px] shrink-0">
16851
+ {timestamp}
16852
+ </span>
16853
+ </div>
16854
+
16855
+ {/* Row 3: Channel + Agent Pill */}
16856
+ <ChannelPill
16857
+ channel={channel}
16858
+ agentName={agentName}
16859
+ isAgentDeleted={isAgentDeleted}
16860
+ isBot={isBot}
16861
+ />
16862
+ </div>
16863
+ </div>
16864
+ );
16865
+ }
16866
+ );
16867
+ ChatListItem.displayName = "ChatListItem";
16868
+
16869
+ export { ChatListItem };
16870
+ `, prefix)
16871
+ },
16872
+ {
16873
+ name: "index.ts",
16874
+ content: prefixTailwindClasses(`export { ChatListItem } from "./chat-list-item";
16875
+ export type {
16876
+ ChatListItemProps,
16877
+ MessageStatus,
16878
+ MessageType,
16879
+ } from "./chat-list-item";
16580
16880
  `, prefix)
16581
16881
  }
16582
16882
  ]
@@ -23518,12 +23818,12 @@ export type { ChatContextValue } from "./types"
23518
23818
  {
23519
23819
  name: "chat-sidebar.tsx",
23520
23820
  content: prefixTailwindClasses(`import * as React from "react"
23521
- import { cn } from "../../../lib/utils"
23821
+ import { cn } from "../../../../lib/utils"
23522
23822
  import { Button } from "../../button"
23523
23823
  import { TextField } from "../../text-field"
23524
23824
  import { Tabs, TabsList, TabsTrigger } from "../../tabs"
23525
23825
  import { Badge } from "../../badge"
23526
- import { ChatListItem, type MessageType } from "../chat-list-item"
23826
+ import { ChatListItem, type MessageType } from "../../chat-list-item"
23527
23827
  import { useChatContext } from "../chat-provider"
23528
23828
  import type { Tab } from "../chat-types"
23529
23829
  import { Search, Plus, CircleAlert } from "lucide-react"
@@ -23846,11 +24146,15 @@ export function ChatFilterPanel({ onClose, onApply }: ChatFilterPanelProps) {
23846
24146
  const isDirty = () => {
23847
24147
  if (selectedAssignees.size !== initialAssignees.current.size) return true
23848
24148
  if (selectedChannels.size !== initialChannels.current.size) return true
23849
- for (const id of selectedAssignees)
23850
- if (!initialAssignees.current.has(id)) return true
23851
- for (const id of selectedChannels)
23852
- if (!initialChannels.current.has(id)) return true
23853
- return false
24149
+ let dirty = false
24150
+ selectedAssignees.forEach((id) => {
24151
+ if (!initialAssignees.current.has(id)) dirty = true
24152
+ })
24153
+ if (dirty) return true
24154
+ selectedChannels.forEach((id) => {
24155
+ if (!initialChannels.current.has(id)) dirty = true
24156
+ })
24157
+ return dirty
23854
24158
  }
23855
24159
 
23856
24160
  const handleBack = () => {
@@ -24231,7 +24535,7 @@ export type { ChatFilterPanelProps } from "./chat-filter-panel"
24231
24535
  {
24232
24536
  name: "chat-new-panel.tsx",
24233
24537
  content: prefixTailwindClasses(`import * as React from "react"
24234
- import { cn } from "../../../lib/utils"
24538
+ import { cn } from "../../../../lib/utils"
24235
24539
  import { Button } from "../../button"
24236
24540
  import { TextField } from "../../text-field"
24237
24541
  import {
@@ -24392,7 +24696,7 @@ export { ChatNewPanel }
24392
24696
  {
24393
24697
  name: "add-contact-modal.tsx",
24394
24698
  content: prefixTailwindClasses(`import * as React from "react"
24395
- import { cn } from "../../../lib/utils"
24699
+ import { cn } from "../../../../lib/utils"
24396
24700
  import { Button } from "../../button"
24397
24701
  import { TextField } from "../../text-field"
24398
24702
  import {
@@ -24933,7 +25237,7 @@ export {
24933
25237
  {
24934
25238
  name: "chat-message-list.tsx",
24935
25239
  content: prefixTailwindClasses(`import * as React from "react"
24936
- import { cn } from "../../../lib/utils"
25240
+ import { cn } from "../../../../lib/utils"
24937
25241
  import { Button } from "../../button"
24938
25242
  import {
24939
25243
  Tooltip,
@@ -24949,8 +25253,8 @@ import {
24949
25253
  File,
24950
25254
  ArrowDown,
24951
25255
  } from "lucide-react"
24952
- import { ChatTimelineDivider } from "../chat-timeline-divider"
24953
- import { DocMedia } from "../doc-media"
25256
+ import { ChatTimelineDivider } from "../../chat-timeline-divider"
25257
+ import { DocMedia } from "../../doc-media"
24954
25258
  import { useChatContext } from "../chat-provider"
24955
25259
  import type { ChatMessage } from "../chat-types"
24956
25260
  import {
@@ -25566,7 +25870,7 @@ export { AssignmentDropdown }
25566
25870
  name: "resolve-button.tsx",
25567
25871
  content: prefixTailwindClasses(`import { useState } from "react"
25568
25872
  import { Check } from "lucide-react"
25569
- import { cn } from "../../../lib/utils"
25873
+ import { cn } from "../../../../lib/utils"
25570
25874
  import { Button } from "../../button"
25571
25875
  import { useChatContext } from "../chat-provider"
25572
25876
 
@@ -25930,7 +26234,7 @@ import {
25930
26234
  TooltipTrigger,
25931
26235
  TooltipArrow,
25932
26236
  } from "../../tooltip"
25933
- import { ChatComposer } from "../chat-composer"
26237
+ import { ChatComposer } from "../../chat-composer"
25934
26238
  import { ComposerAttachmentPreview } from "./composer-attachment-preview"
25935
26239
  import { CannedMessagesDropdown } from "./canned-messages"
25936
26240
  import { useChatContext } from "../chat-provider"
@@ -27597,7 +27901,7 @@ export function resolveVars(
27597
27901
  {
27598
27902
  name: "chat-app.tsx",
27599
27903
  content: prefixTailwindClasses(`import * as React from "react"
27600
- import { cn } from "../../../lib/utils"
27904
+ import { cn } from "../../../../lib/utils"
27601
27905
  import { Button } from "../../button"
27602
27906
  import {
27603
27907
  TooltipProvider,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "myoperator-ui",
3
- "version": "0.0.222",
3
+ "version": "0.0.223-beta.1",
4
4
  "description": "CLI for adding myOperator UI components to your project",
5
5
  "type": "module",
6
6
  "exports": "./dist/index.js",