@buerokratt-ria/common-gui-components 0.0.42 → 0.0.44

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/CHANGELOG.md CHANGED
@@ -4,6 +4,17 @@ All changes to this project will be documented in this file.
4
4
 
5
5
  ## Template [MajorVersion.MediterraneanVersion.MinorVersion] - DD-MM-YYYY
6
6
 
7
+ ## [0.0.44] - 26.02.2026
8
+
9
+ - Removed _ from chosen CSA's list
10
+
11
+ ## [0.0.43] - 06.02.2026
12
+
13
+ - Prevent url fetch in markdownify for non image urls
14
+ - Added Comment Settings to get-chat-by-id
15
+ - Enhance eventGroup Function
16
+ - Reset Pagination on search change
17
+
7
18
  ## [0.0.42] - 04.02.2026
8
19
 
9
20
  - Fix comment display in chat content view
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@buerokratt-ria/common-gui-components",
3
- "version": "0.0.42",
3
+ "version": "0.0.44",
4
4
  "description": "Common GUI components and pre defined templates.",
5
5
  "main": "index.ts",
6
6
  "author": "ExiRai",
@@ -340,23 +340,7 @@ const ChatHistory: FC<PropsWithChildren<HistoryProps>> = ({
340
340
  chatId: passedChatId,
341
341
  }),
342
342
  onSuccess: (res: any) => {
343
- setSelectedChat({
344
- ...res.data.response,
345
- comment: selectedChat?.comment,
346
- commentAddedDate: selectedChat?.commentAddedDate,
347
- commentAuthor: selectedChat?.commentAuthor,
348
- labels: selectedChat?.labels,
349
- lastMessageEvent: selectedChat?.lastMessageEvent,
350
- contactsMessage: selectedChat?.contactsMessage,
351
- isFiveRatingScale: selectedChat?.isFiveRatingScale,
352
- istest: selectedChat?.istest,
353
- nps: selectedChat?.nps,
354
- userDisplayName: selectedChat?.userDisplayName,
355
- customerSupportFirstName: selectedChat?.customerSupportFirstName,
356
- customerSupportLastName: selectedChat?.customerSupportLastName,
357
- allCsa: selectedChat?.allCsa,
358
- totalPages: selectedChat?.totalPages,
359
- });
343
+ setSelectedChat(res.data.response);
360
344
  setChatState(res.data.response);
361
345
  },
362
346
  });
@@ -372,7 +356,6 @@ const ChatHistory: FC<PropsWithChildren<HistoryProps>> = ({
372
356
  }),
373
357
  onSuccess: (res: any) => {
374
358
  setCustomerSupportAgents([
375
- {label: '-', value: '-'},
376
359
  {label: 'Bürokratt', value: 'chatbot'},
377
360
  ...res.data.response.map((item) => ({
378
361
  label: [item.firstName, item.lastName].join(' ').trim(),
@@ -1015,6 +998,11 @@ const ChatHistory: FC<PropsWithChildren<HistoryProps>> = ({
1015
998
  name="searchChats"
1016
999
  placeholder={t('chat.history.searchChats') + '...'}
1017
1000
  onChange={(e) => {
1001
+ setPagination({ pageIndex: 0, pageSize: pagination.pageSize });
1002
+ setSearchParams((params) => {
1003
+ params.set("page", "1");
1004
+ return params;
1005
+ });
1018
1006
  setSearch(e.target.value);
1019
1007
  debouncedGetAllEnded(e.target.value);
1020
1008
  }}
@@ -1125,18 +1113,27 @@ const ChatHistory: FC<PropsWithChildren<HistoryProps>> = ({
1125
1113
  setSearchParams((params) => {
1126
1114
  params.delete('customerSupportIds');
1127
1115
  params.set('page', '1');
1128
- selection?.forEach((s) =>
1129
- params.append('customerSupportIds', s.value)
1130
- );
1116
+ selection?.forEach((s) => {
1117
+ params.append("customerSupportIds", s.value);
1118
+ if (s.value === "chatbot") params.append("customerSupportIds", "-");
1119
+ return params;
1120
+ });
1131
1121
  return params;
1132
1122
  });
1133
1123
 
1134
1124
  setPagination({pageIndex: 0, pageSize: pagination.pageSize});
1135
1125
 
1126
+ const customerSupportIds =
1127
+ selection?.reduce((acc, s) => {
1128
+ acc.push(s.value);
1129
+ if (s.value === "chatbot") acc.push("-");
1130
+ return acc;
1131
+ }, []) || [];
1132
+
1136
1133
  getAllEndedChats.mutate({
1137
1134
  startDate,
1138
1135
  endDate,
1139
- customerSupportIds: selection?.map((s) => s.value) || [],
1136
+ customerSupportIds: customerSupportIds,
1140
1137
  pagination: {pageIndex: 0, pageSize: pagination.pageSize},
1141
1138
  sorting,
1142
1139
  search,
@@ -1,5 +1,5 @@
1
- import React, { useState } from "react";
2
1
  import Markdown from "markdown-to-jsx";
2
+ import React, { useState } from "react";
3
3
  import sanitizeHtml from "sanitize-html";
4
4
 
5
5
  interface MarkdownifyProps {
@@ -7,6 +7,27 @@ interface MarkdownifyProps {
7
7
  sanitizeLinks?: boolean;
8
8
  }
9
9
 
10
+ const isValidImageUrl = (s: string): boolean => {
11
+ try {
12
+ const u = new URL(s);
13
+ if (!/^(https?|data):$/i.test(u.protocol)) return false;
14
+ if (s.startsWith("data:image/")) {
15
+ return /^data:image\/(png|jpe?g|gif|webp|svg\+xml|bmp);(base64,|charset=utf-8;)/i.test(s);
16
+ }
17
+ const path = u.pathname.toLowerCase();
18
+ const search = u.search.toLowerCase();
19
+ if (/\.(png|jpe?g|gif|webp|svg|ico|bmp|tiff?|avif|heic|heif|apng)([?#]|$)/i.test(path)) {
20
+ return true;
21
+ }
22
+ if (/\/(images?|img|photos?|pictures?|media|uploads?|thumb|avatar)\//i.test(path)) {
23
+ return true;
24
+ }
25
+ return /[?&](format|type|image|img|photo)=(png|jpe?g|gif|webp|svg|ico)/i.test(search);
26
+ } catch {
27
+ return false;
28
+ }
29
+ };
30
+
10
31
  const LinkPreview: React.FC<{
11
32
  href: string;
12
33
  children: React.ReactNode;
@@ -27,17 +48,25 @@ const LinkPreview: React.FC<{
27
48
  );
28
49
  }
29
50
 
30
- return !hasError ? (
51
+ if (!isValidImageUrl(href)) {
52
+ return (
53
+ <a href={href} target="_blank" rel="noopener noreferrer">
54
+ {children}
55
+ </a>
56
+ );
57
+ }
58
+
59
+ return hasError ? (
60
+ <a href={href} target="_blank" rel="noopener noreferrer">
61
+ {children}
62
+ </a>
63
+ ) : (
31
64
  <img
32
65
  src={href}
33
66
  alt={typeof children === "string" ? children : "Preview"}
34
67
  style={{ maxWidth: "100%", height: "auto", borderRadius: "20px" }}
35
68
  onError={() => setHasError(true)}
36
69
  />
37
- ) : (
38
- <a href={href} target="_blank" rel="noopener noreferrer">
39
- {children}
40
- </a>
41
70
  );
42
71
  };
43
72
 
@@ -53,8 +82,14 @@ function formatMessage(message?: string): string {
53
82
  .replaceAll(/\\?\$v\w*/g, "")
54
83
  .replaceAll(/\\?\$g\w*/g, "");
55
84
 
56
- return filteredMessage
57
- .replaceAll(/&#x([0-9A-Fa-f]+);/g, (_, hex: string) => String.fromCharCode(parseInt(hex, 16)))
85
+ const dataImagePattern = /((?:^|\s))(data:image\/[a-z0-9+]+;[^)\s]+)/gi;
86
+ const finalMessage = filteredMessage.replaceAll(
87
+ dataImagePattern,
88
+ (_, prefix, dataUrl) => `${prefix}[image](${dataUrl})`,
89
+ );
90
+
91
+ return finalMessage
92
+ .replaceAll(/&#x([0-9A-F]+);/gi, (_, hex: string) => String.fromCharCode(parseInt(hex, 16)))
58
93
  .replaceAll("&amp;", "&")
59
94
  .replaceAll("&gt;", ">")
60
95
  .replaceAll("&lt;", "<")
@@ -62,7 +97,7 @@ function formatMessage(message?: string): string {
62
97
  .replaceAll("&#39;", "'")
63
98
  .replaceAll("&apos;", "'")
64
99
  .replaceAll(/(^|\n)(\d{4})\.\s/g, (match, prefix, year) => {
65
- const remainingText = filteredMessage.substring(filteredMessage.indexOf(match) + match.length);
100
+ const remainingText = finalMessage.substring(finalMessage.indexOf(match) + match.length);
66
101
  const sentenceEnd = remainingText.indexOf("\n\n");
67
102
  if (sentenceEnd !== -1) {
68
103
  const currentSentence = remainingText.substring(0, sentenceEnd);
@@ -72,7 +107,7 @@ function formatMessage(message?: string): string {
72
107
  }
73
108
  return `${prefix}${year}\\. `;
74
109
  })
75
- .replaceAll(/(?<=\n)\d+\.\s/g, hasSpecialFormat(filteredMessage) ? "\n\n$&" : "$&")
110
+ .replaceAll(/(?<=\n)\d+\.\s/g, hasSpecialFormat(finalMessage) ? "\n\n$&" : "$&")
76
111
  .replaceAll(/^(\s+)/g, (match) => match.replaceAll(" ", "&nbsp;"));
77
112
  }
78
113
 
@@ -194,7 +194,52 @@ const HistoricalChat: FC<ChatProps> = ({
194
194
 
195
195
  const eventGroup = (group: GroupedMessage) => {
196
196
  return group.messages.map((message) => {
197
- return <ChatEvent key={message.id} message={message} />;
197
+ if (message.event) {
198
+ return <ChatEvent key={message.id} message={message} />;
199
+ } else {
200
+ return (
201
+ <div key={message.id}>
202
+ {group.name.trim() && (
203
+ <div className="historical-chat__group-header">
204
+ <div className="historical-chat__group-event-initials">
205
+ {group.type === "buerokratt" || group.type === "chatbot" ? (
206
+ <BykLogoWhite height={24} />
207
+ ) : (
208
+ (() => {
209
+ if (group.name) {
210
+ const initials = group.name
211
+ .split(" ")
212
+ .map((n) => n[0])
213
+ .join("")
214
+ .toUpperCase();
215
+ return <>{initials}</>;
216
+ } else {
217
+ return <></>;
218
+ }
219
+ })()
220
+ )}
221
+ </div>
222
+ {group.name && (
223
+ <div className="historical-chat__group-event-name">
224
+ {group.name}
225
+ {group.title.length > 0 && <div className="title">{group.title}</div>}
226
+ </div>
227
+ )}
228
+ </div>
229
+ )}
230
+ <div className="historical-chat__messages">
231
+ <ChatMessage
232
+ message={message}
233
+ key={`${message.id ?? ""}`}
234
+ toastContext={toastContext}
235
+ onMessageClick={(message) => {
236
+ onMessageClick?.(message);
237
+ }}
238
+ />
239
+ </div>
240
+ </div>
241
+ );
242
+ }
198
243
  });
199
244
  };
200
245