prime-ui-kit 0.21.4 → 0.21.5

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.
@@ -0,0 +1,129 @@
1
+ /** What happened to the client, right in the dialog: who took it, a stage it moved to, a manager who joined; events in a row stand as one group, a new one rises in at the end — `isService`, `Chat.Service`, `Chat.ServiceIcon`, `Chat.ServiceChange`, `time`. */
2
+ import { Badge, Button, Card, Chat, Icon, Message } from "prime-ui-kit";
3
+ import * as React from "react";
4
+
5
+ import styles from "./examples.module.css";
6
+
7
+ const NOW = new Date(2026, 9, 15, 11, 0);
8
+ const at = (hour: number, minute: number) => new Date(2026, 9, 15, hour, minute);
9
+
10
+ type DialogItem =
11
+ | { id: string; kind: "message"; author: "client" | "agent"; sentAt: Date; text: string }
12
+ | { id: string; kind: "taken"; sentAt: Date }
13
+ | { id: string; kind: "stage"; sentAt: Date; from: string; to: string }
14
+ | { id: string; kind: "joined"; sentAt: Date; manager: string };
15
+
16
+ const DIALOG: DialogItem[] = [
17
+ {
18
+ id: "ev-1",
19
+ kind: "message",
20
+ author: "client",
21
+ sentAt: at(10, 12),
22
+ text: "Здравствуйте! Ищу склад под аренду, 100–150 м², с 1 ноября.",
23
+ },
24
+ { id: "ev-2", kind: "taken", sentAt: at(10, 12) },
25
+ { id: "ev-3", kind: "stage", sentAt: at(10, 12), from: "Лид", to: "Арендатор" },
26
+ {
27
+ id: "ev-4",
28
+ kind: "message",
29
+ author: "agent",
30
+ sentAt: at(10, 13),
31
+ text: "Добрый день! Есть склад 120 м² на Складской, 4, свободен с 25 октября. Прислать планировку?",
32
+ },
33
+ {
34
+ id: "ev-5",
35
+ kind: "message",
36
+ author: "client",
37
+ sentAt: at(10, 15),
38
+ text: "Да, и хотелось бы обсудить скидку за год вперёд.",
39
+ },
40
+ ];
41
+
42
+ const JOINED: DialogItem = {
43
+ id: "ev-6",
44
+ kind: "joined",
45
+ sentAt: at(10, 16),
46
+ manager: "Анна Котова",
47
+ };
48
+
49
+ function renderItem(item: DialogItem) {
50
+ if (item.kind === "taken") {
51
+ return (
52
+ <Chat.Service time={item.sentAt}>
53
+ <Chat.ServiceIcon>
54
+ <Icon name="object.bot" />
55
+ </Chat.ServiceIcon>
56
+ ИИ-агент взял клиента
57
+ </Chat.Service>
58
+ );
59
+ }
60
+ if (item.kind === "stage") {
61
+ return (
62
+ <Chat.Service time={item.sentAt}>
63
+ <Chat.ServiceIcon>
64
+ <Icon name="object.funnel" />
65
+ </Chat.ServiceIcon>
66
+ ИИ-агент перевёл клиента
67
+ <Chat.ServiceChange
68
+ from={<Badge.Root color="gray">{item.from}</Badge.Root>}
69
+ to={<Badge.Root color="green">{item.to}</Badge.Root>}
70
+ />
71
+ </Chat.Service>
72
+ );
73
+ }
74
+ if (item.kind === "joined") {
75
+ return (
76
+ <Chat.Service time={item.sentAt}>
77
+ <Chat.ServiceIcon>
78
+ <Icon name="object.user" />
79
+ </Chat.ServiceIcon>
80
+ Менеджер {item.manager} подключилась к диалогу
81
+ </Chat.Service>
82
+ );
83
+ }
84
+ return (
85
+ <Message.Root
86
+ direction={item.author === "agent" ? "outgoing" : "incoming"}
87
+ time={item.sentAt}
88
+ status={item.author === "agent" ? "read" : undefined}
89
+ >
90
+ <Message.Text>{item.text}</Message.Text>
91
+ </Message.Root>
92
+ );
93
+ }
94
+
95
+ export default function ChatEventsExample() {
96
+ const [items, setItems] = React.useState(DIALOG);
97
+ const joined = items.includes(JOINED);
98
+
99
+ return (
100
+ <div className={styles.live}>
101
+ <div className={styles.liveBar}>
102
+ <Button.Root
103
+ variant="soft"
104
+ disabled={joined}
105
+ onClick={() => setItems((current) => [...current, JOINED])}
106
+ >
107
+ <Button.Icon>
108
+ <Icon name="action.addContact" />
109
+ </Button.Icon>
110
+ Подключить менеджера
111
+ </Button.Root>
112
+ </div>
113
+ <Card.Root className={styles.short}>
114
+ <Chat.Root>
115
+ <Chat.Messages
116
+ aria-label="Диалог с клиентом «Склад на Складской»"
117
+ now={NOW}
118
+ items={items}
119
+ getKey={(item: DialogItem) => item.id}
120
+ getAuthor={(item: DialogItem) => (item.kind === "message" ? item.author : "system")}
121
+ getDate={(item: DialogItem) => item.sentAt}
122
+ isService={(item: DialogItem) => item.kind !== "message"}
123
+ renderMessage={renderItem}
124
+ />
125
+ </Chat.Root>
126
+ </Card.Root>
127
+ </div>
128
+ );
129
+ }
@@ -1,6 +1,7 @@
1
- /** A support messenger as a work screen: the title and the unread count in its top bar, the dialogs with a search over them beside the conversation (one pane at a time on a phone), history that loads above as you scroll up, the unread band, reply and edit from the menu, the typing signal both ways, unread counts from what was seen, and the Composer whose messages rise into the list while the client answers — `items`, `renderMessage`, `hasOlder`, `onLoadOlder`, `typing`. */
1
+ /** A support messenger as a work screen: the title and the unread count in its top bar, the dialogs with a search over them beside the conversation (one pane at a time on a phone), history that loads above as you scroll up, the unread band, reply and edit from the menu, the typing signal both ways, the request's events (an agent took it, its status moved, you joined) between the messages, unread counts from what was seen, and the Composer whose messages rise into the list while the client answers — `items`, `renderMessage`, `isService`, `hasOlder`, `onLoadOlder`, `typing`. */
2
2
  import {
3
3
  Avatar,
4
+ Badge,
4
5
  Card,
5
6
  Chat,
6
7
  ChatList,
@@ -28,7 +29,13 @@ type Content =
28
29
  | { kind: "text"; text: string }
29
30
  | { kind: "media"; items: GalleryItem[]; caption?: string }
30
31
  | { kind: "voice"; src: string; waveform: number[]; duration: number }
31
- | { kind: "file"; name: string; size: number; href: string };
32
+ | { kind: "file"; name: string; size: number; href: string }
33
+ | {
34
+ kind: "event";
35
+ icon: "object.bot" | "object.funnel" | "object.user";
36
+ text: string;
37
+ change?: { from: string; to: string };
38
+ };
32
39
 
33
40
  type ChatMessage = Content & {
34
41
  id: string;
@@ -129,6 +136,31 @@ const HISTORY: ChatMessage[] = [
129
136
  ],
130
137
  caption: "Вот так выглядит",
131
138
  },
139
+ {
140
+ id: "e1",
141
+ from: "client",
142
+ sentAt: at(7, 10, 14),
143
+ kind: "event",
144
+ icon: "object.bot",
145
+ text: "ИИ-агент взял обращение",
146
+ },
147
+ {
148
+ id: "e2",
149
+ from: "client",
150
+ sentAt: at(7, 10, 14),
151
+ kind: "event",
152
+ icon: "object.funnel",
153
+ text: "ИИ-агент перевёл обращение",
154
+ change: { from: "Новое", to: "В работе" },
155
+ },
156
+ {
157
+ id: "e3",
158
+ from: "me",
159
+ sentAt: at(7, 10, 19),
160
+ kind: "event",
161
+ icon: "object.user",
162
+ text: "Вы подключились к диалогу",
163
+ },
132
164
  {
133
165
  id: "m3",
134
166
  from: "me",
@@ -228,6 +260,8 @@ const excerpt = (message: ChatMessage) => {
228
260
  );
229
261
  case "file":
230
262
  return message.name;
263
+ case "event":
264
+ return message.text;
231
265
  }
232
266
  };
233
267
 
@@ -260,6 +294,8 @@ const renderContent = (message: ChatMessage) => {
260
294
  );
261
295
  case "file":
262
296
  return <Message.File name={message.name} fileSize={message.size} href={message.href} />;
297
+ case "event":
298
+ return null;
263
299
  }
264
300
  };
265
301
 
@@ -522,6 +558,23 @@ export default function ChatScreenExample() {
522
558
  // re-renders the one message it touches. A dialog loads its own messages, so a row never
523
559
  // outlives the contact it names; a reply's original comes from the list (`entry.find`).
524
560
  const renderMessage = (message: ChatMessage, entry: ChatMessageEntry<ChatMessage>) => {
561
+ // What happened to the request (an agent took it, its status moved, you joined): a row.
562
+ if (message.kind === "event") {
563
+ return (
564
+ <Chat.Service time={message.sentAt}>
565
+ <Chat.ServiceIcon>
566
+ <Icon name={message.icon} />
567
+ </Chat.ServiceIcon>
568
+ {message.text}
569
+ {message.change ? (
570
+ <Chat.ServiceChange
571
+ from={<Badge.Root color="gray">{message.change.from}</Badge.Root>}
572
+ to={<Badge.Root color="blue">{message.change.to}</Badge.Root>}
573
+ />
574
+ ) : null}
575
+ </Chat.Service>
576
+ );
577
+ }
525
578
  const own = message.from === "me";
526
579
  const original = message.replyTo ? entry.find(message.replyTo) : undefined;
527
580
  return (
@@ -645,6 +698,7 @@ export default function ChatScreenExample() {
645
698
  getKey={(message) => message.id}
646
699
  getAuthor={(message) => message.from}
647
700
  getDate={(message) => message.sentAt}
701
+ isService={(message) => message.kind === "event"}
648
702
  renderMessage={renderMessage}
649
703
  unread={FIRST_UNREAD}
650
704
  now={NOW}