@antscorp/antsomi-ui 2.0.121 → 2.0.122

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
@@ -1,92 +1,92 @@
1
- ### Changelog
2
-
3
- All notable changes to this project will be documented in this file. Dates are displayed in UTC.
4
-
5
- Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
6
-
7
- #### [v2.0.115](https://bitbucket.org/git-vn/at.packages.frontend/compare/v2.0.115..v2.0.64-staging.60)
8
-
9
- - feat: update version antsomi-locales [`7d44aae`](https://bitbucket.org/git-vn/at.packages.frontend/commits/7d44aae3055b3b1bbfb772e2ed3afd5fa920fd05)
10
- - chore: update package antsomi locales [`30e406b`](https://bitbucket.org/git-vn/at.packages.frontend/commits/30e406ba9010701346823354f2829d0fca3c24be)
11
- - feat: update version antsomi-locales [`187cb53`](https://bitbucket.org/git-vn/at.packages.frontend/commits/187cb5342342de8b2dea8b3a19c4a4029e4a3f7f)
12
-
13
- #### [v2.0.64-staging.60](https://bitbucket.org/git-vn/at.packages.frontend/compare/v2.0.64-staging.60..v2.0.64-staging.58)
14
-
15
- > 20 March 2026
16
-
17
- - feat: update message activity timeline [`7a3bc15`](https://bitbucket.org/git-vn/at.packages.frontend/commits/7a3bc15dd2e2eb7caf91e6bf064edd48e97dcdb2)
18
- - feat: add fallback scheme_code [`ecb8e67`](https://bitbucket.org/git-vn/at.packages.frontend/commits/ecb8e67a56b93610df913c2f7c37d89b8be98ad6)
19
- - feat: update extra keys [`da5b04e`](https://bitbucket.org/git-vn/at.packages.frontend/commits/da5b04e9e1c6b5d0c3584c4e08ffe70733ad4e77)
20
-
21
- #### [v2.0.64-staging.58](https://bitbucket.org/git-vn/at.packages.frontend/compare/v2.0.64-staging.58..v2.0.64-staging.54)
22
-
23
- > 17 March 2026
24
-
25
- - feat: add event tg [`3d5672b`](https://bitbucket.org/git-vn/at.packages.frontend/commits/3d5672bf798efc459cec8c2866692ecc48be1141)
26
- - feat: update message activity timeline leads [`3b1590e`](https://bitbucket.org/git-vn/at.packages.frontend/commits/3b1590ee8c5aa8a1d671082f6fd72d9d85b6414b)
27
- - feat: update message activity timeline leads [`8b4d22c`](https://bitbucket.org/git-vn/at.packages.frontend/commits/8b4d22cf8d1ad4a4cc0e7c36dcd8306ae9e2f7d7)
28
-
29
- #### [v2.0.64-staging.54](https://bitbucket.org/git-vn/at.packages.frontend/compare/v2.0.64-staging.54..v2.0.22)
30
-
31
- > 30 January 2026
32
-
33
- - fix: build search logic [`#27`](https://bitbucket.org/git-vn/at.packages.frontend/pull-requests/27)
34
- - feat: add report area chart [`4a0fad4`](https://bitbucket.org/git-vn/at.packages.frontend/commits/4a0fad4b405425ef03b9cf6038f6e9389f44a366)
35
- - chore: update storybook version [`d13f194`](https://bitbucket.org/git-vn/at.packages.frontend/commits/d13f19462c2fe117934df7fe2e09fd0aa95a0167)
36
- - feat: init antsomi-genai [`d898d07`](https://bitbucket.org/git-vn/at.packages.frontend/commits/d898d07f09cf6436ff9ba2a5430e08fc44190781)
37
-
38
- ### [v2.0.22](https://bitbucket.org/git-vn/at.packages.frontend/compare/v2.0.22..v1.0.10)
39
-
40
- > 19 January 2023
41
-
42
- #### [v1.0.10](https://bitbucket.org/git-vn/at.packages.frontend/compare/v1.0.10..v1.0.9)
43
-
44
- > 20 June 2024
45
-
46
- #### [v1.0.9](https://bitbucket.org/git-vn/at.packages.frontend/compare/v1.0.9..v1.0.8)
47
-
48
- > 20 June 2024
49
-
50
- #### [v1.0.8](https://bitbucket.org/git-vn/at.packages.frontend/compare/v1.0.8..v1.0.3)
51
-
52
- > 20 June 2024
53
-
54
- - feat(SelectAccount): add component [`2f1dea0`](https://bitbucket.org/git-vn/at.packages.frontend/commits/2f1dea035af07a01a6be8da47b531d52d2977bbe)
55
- - fix: update style for calendar selection [`4347b91`](https://bitbucket.org/git-vn/at.packages.frontend/commits/4347b91f5bbe31caeb64478116c1822fe443f359)
56
- - feat(EditorTab): use modal async in onCloseTab [`76aece8`](https://bitbucket.org/git-vn/at.packages.frontend/commits/76aece8cc7a44385d63019058daa1f8477c8fbcb)
57
-
58
- #### [v1.0.3](https://bitbucket.org/git-vn/at.packages.frontend/compare/v1.0.3..v1.0.2)
59
-
60
- > 17 January 2023
61
-
62
- - update timeline package [`e198927`](https://bitbucket.org/git-vn/at.packages.frontend/commits/e198927d83931f9155e90ab9a1d9d3a5ac2989c4)
63
- - update-ticket-editor [`38dd7b0`](https://bitbucket.org/git-vn/at.packages.frontend/commits/38dd7b0a7b2b0b58a63e725419e9b057e053f44f)
64
- - update-upload-file-ticket-editor [`bb2f2ef`](https://bitbucket.org/git-vn/at.packages.frontend/commits/bb2f2ef1c46f4239ad716c897e53856a6368ea7d)
65
-
66
- #### v1.0.2
67
-
68
- > 4 January 2023
69
-
70
- - Sprint10/vinlt/feature/date advanced [`#24`](https://bitbucket.org/git-vn/at.packages.frontend/pull-requests/24)
71
- - Sprint10/sangndd/feature/extend value matches any [`#23`](https://bitbucket.org/git-vn/at.packages.frontend/pull-requests/23)
72
- - Sprint10/sangndd/feature/extend value matches any [`#22`](https://bitbucket.org/git-vn/at.packages.frontend/pull-requests/22)
73
- - [Scatter chart] Max X-axis giá trị bị lặp [`#20`](https://bitbucket.org/git-vn/at.packages.frontend/pull-requests/20)
74
- - Sprint8/vinlt/feature/show data label short [`#18`](https://bitbucket.org/git-vn/at.packages.frontend/pull-requests/18)
75
- - Fix duplicate axis max value [`#17`](https://bitbucket.org/git-vn/at.packages.frontend/pull-requests/17)
76
- - [Antalyser] Một số bug khi nhập axis giá trị thập phân [`#16`](https://bitbucket.org/git-vn/at.packages.frontend/pull-requests/16)
77
- - Sprint6/vinlt/fixbug/chart [`#15`](https://bitbucket.org/git-vn/at.packages.frontend/pull-requests/15)
78
- - - Axis allows input of decimal values [`#14`](https://bitbucket.org/git-vn/at.packages.frontend/pull-requests/14)
79
- - Sprint4/sangndd/feature/icons [`#12`](https://bitbucket.org/git-vn/at.packages.frontend/pull-requests/12)
80
- - Sprint4/sangndd/feature/icons [`#11`](https://bitbucket.org/git-vn/at.packages.frontend/pull-requests/11)
81
- - Sprint4/thanghn/fixbug/pivot alias border [`#10`](https://bitbucket.org/git-vn/at.packages.frontend/pull-requests/10)
82
- - fix scroll dimension and total column [`#9`](https://bitbucket.org/git-vn/at.packages.frontend/pull-requests/9)
83
- - [ANTALYSER][Filter][Khi filter filed có type date và semantic là "Month day" thì không load được danh sách các ngày trong tháng] [`#7`](https://bitbucket.org/git-vn/at.packages.frontend/pull-requests/7)
84
- - [ANTALYSER][Pivot chart][Tính năng Add border shadow chưa hoạt động] [`#8`](https://bitbucket.org/git-vn/at.packages.frontend/pull-requests/8)
85
- - Fix bug [ANTALYSER][Scatter chart][Label ở cột X và Y chưa đổi theo alias name đã change ở metric] [`#6`](https://bitbucket.org/git-vn/at.packages.frontend/pull-requests/6)
86
- - Fix bug [Pivot chart] Row color không hiển thị được khi chọn hiển thị data của metric là Bar [`#5`](https://bitbucket.org/git-vn/at.packages.frontend/pull-requests/5)
87
- - Fix bug package antscorp/charts [`#1`](https://bitbucket.org/git-vn/at.packages.frontend/pull-requests/1)
88
- - Fix bug package antscorp/chart [`#2`](https://bitbucket.org/git-vn/at.packages.frontend/pull-requests/2)
89
- - table component [`#1`](https://bitbucket.org/git-vn/at.packages.frontend/pull-requests/1)
90
- - Update map indonesia [`fc94208`](https://bitbucket.org/git-vn/at.packages.frontend/commits/fc9420838b3f4087a5763017e9bf7e72ee8c5e31)
91
- - Update geomap [`43f3098`](https://bitbucket.org/git-vn/at.packages.frontend/commits/43f3098506f30a215dc2b30734590e5c55f8b490)
92
- - sync package master [`7476118`](https://bitbucket.org/git-vn/at.packages.frontend/commits/7476118de2fcfa1bd6b0a9e3c0f887b29fd149b6)
1
+ ### Changelog
2
+
3
+ All notable changes to this project will be documented in this file. Dates are displayed in UTC.
4
+
5
+ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
6
+
7
+ #### [v2.0.122](https://bitbucket.org/git-vn/at.packages.frontend/compare/v2.0.122..v2.0.64-staging.60)
8
+
9
+ - feat: update version antsomi-locales [`7d44aae`](https://bitbucket.org/git-vn/at.packages.frontend/commits/7d44aae3055b3b1bbfb772e2ed3afd5fa920fd05)
10
+ - feat: scaffold antsomi ui v2 facade [`5bf7910`](https://bitbucket.org/git-vn/at.packages.frontend/commits/5bf7910250bbf5577bfb95b3f93c14f23c4f9584)
11
+ - feat: add antsomi light theme provider [`04e7fca`](https://bitbucket.org/git-vn/at.packages.frontend/commits/04e7fcae7ccb861d97e43392ab4966a11c7e8774)
12
+
13
+ #### [v2.0.64-staging.60](https://bitbucket.org/git-vn/at.packages.frontend/compare/v2.0.64-staging.60..v2.0.64-staging.58)
14
+
15
+ > 20 March 2026
16
+
17
+ - feat: update message activity timeline [`7a3bc15`](https://bitbucket.org/git-vn/at.packages.frontend/commits/7a3bc15dd2e2eb7caf91e6bf064edd48e97dcdb2)
18
+ - feat: add fallback scheme_code [`ecb8e67`](https://bitbucket.org/git-vn/at.packages.frontend/commits/ecb8e67a56b93610df913c2f7c37d89b8be98ad6)
19
+ - feat: update extra keys [`da5b04e`](https://bitbucket.org/git-vn/at.packages.frontend/commits/da5b04e9e1c6b5d0c3584c4e08ffe70733ad4e77)
20
+
21
+ #### [v2.0.64-staging.58](https://bitbucket.org/git-vn/at.packages.frontend/compare/v2.0.64-staging.58..v2.0.64-staging.54)
22
+
23
+ > 17 March 2026
24
+
25
+ - feat: add event tg [`3d5672b`](https://bitbucket.org/git-vn/at.packages.frontend/commits/3d5672bf798efc459cec8c2866692ecc48be1141)
26
+ - feat: update message activity timeline leads [`3b1590e`](https://bitbucket.org/git-vn/at.packages.frontend/commits/3b1590ee8c5aa8a1d671082f6fd72d9d85b6414b)
27
+ - feat: update message activity timeline leads [`8b4d22c`](https://bitbucket.org/git-vn/at.packages.frontend/commits/8b4d22cf8d1ad4a4cc0e7c36dcd8306ae9e2f7d7)
28
+
29
+ #### [v2.0.64-staging.54](https://bitbucket.org/git-vn/at.packages.frontend/compare/v2.0.64-staging.54..v2.0.22)
30
+
31
+ > 30 January 2026
32
+
33
+ - fix: build search logic [`#27`](https://bitbucket.org/git-vn/at.packages.frontend/pull-requests/27)
34
+ - feat: add report area chart [`4a0fad4`](https://bitbucket.org/git-vn/at.packages.frontend/commits/4a0fad4b405425ef03b9cf6038f6e9389f44a366)
35
+ - chore: update storybook version [`d13f194`](https://bitbucket.org/git-vn/at.packages.frontend/commits/d13f19462c2fe117934df7fe2e09fd0aa95a0167)
36
+ - feat: init antsomi-genai [`d898d07`](https://bitbucket.org/git-vn/at.packages.frontend/commits/d898d07f09cf6436ff9ba2a5430e08fc44190781)
37
+
38
+ ### [v2.0.22](https://bitbucket.org/git-vn/at.packages.frontend/compare/v2.0.22..v1.0.10)
39
+
40
+ > 19 January 2023
41
+
42
+ #### [v1.0.10](https://bitbucket.org/git-vn/at.packages.frontend/compare/v1.0.10..v1.0.9)
43
+
44
+ > 20 June 2024
45
+
46
+ #### [v1.0.9](https://bitbucket.org/git-vn/at.packages.frontend/compare/v1.0.9..v1.0.8)
47
+
48
+ > 20 June 2024
49
+
50
+ #### [v1.0.8](https://bitbucket.org/git-vn/at.packages.frontend/compare/v1.0.8..v1.0.3)
51
+
52
+ > 20 June 2024
53
+
54
+ - feat(SelectAccount): add component [`2f1dea0`](https://bitbucket.org/git-vn/at.packages.frontend/commits/2f1dea035af07a01a6be8da47b531d52d2977bbe)
55
+ - fix: update style for calendar selection [`4347b91`](https://bitbucket.org/git-vn/at.packages.frontend/commits/4347b91f5bbe31caeb64478116c1822fe443f359)
56
+ - feat(EditorTab): use modal async in onCloseTab [`76aece8`](https://bitbucket.org/git-vn/at.packages.frontend/commits/76aece8cc7a44385d63019058daa1f8477c8fbcb)
57
+
58
+ #### [v1.0.3](https://bitbucket.org/git-vn/at.packages.frontend/compare/v1.0.3..v1.0.2)
59
+
60
+ > 17 January 2023
61
+
62
+ - update timeline package [`e198927`](https://bitbucket.org/git-vn/at.packages.frontend/commits/e198927d83931f9155e90ab9a1d9d3a5ac2989c4)
63
+ - update-ticket-editor [`38dd7b0`](https://bitbucket.org/git-vn/at.packages.frontend/commits/38dd7b0a7b2b0b58a63e725419e9b057e053f44f)
64
+ - update-upload-file-ticket-editor [`bb2f2ef`](https://bitbucket.org/git-vn/at.packages.frontend/commits/bb2f2ef1c46f4239ad716c897e53856a6368ea7d)
65
+
66
+ #### v1.0.2
67
+
68
+ > 4 January 2023
69
+
70
+ - Sprint10/vinlt/feature/date advanced [`#24`](https://bitbucket.org/git-vn/at.packages.frontend/pull-requests/24)
71
+ - Sprint10/sangndd/feature/extend value matches any [`#23`](https://bitbucket.org/git-vn/at.packages.frontend/pull-requests/23)
72
+ - Sprint10/sangndd/feature/extend value matches any [`#22`](https://bitbucket.org/git-vn/at.packages.frontend/pull-requests/22)
73
+ - [Scatter chart] Max X-axis giá trị bị lặp [`#20`](https://bitbucket.org/git-vn/at.packages.frontend/pull-requests/20)
74
+ - Sprint8/vinlt/feature/show data label short [`#18`](https://bitbucket.org/git-vn/at.packages.frontend/pull-requests/18)
75
+ - Fix duplicate axis max value [`#17`](https://bitbucket.org/git-vn/at.packages.frontend/pull-requests/17)
76
+ - [Antalyser] Một số bug khi nhập axis giá trị thập phân [`#16`](https://bitbucket.org/git-vn/at.packages.frontend/pull-requests/16)
77
+ - Sprint6/vinlt/fixbug/chart [`#15`](https://bitbucket.org/git-vn/at.packages.frontend/pull-requests/15)
78
+ - - Axis allows input of decimal values [`#14`](https://bitbucket.org/git-vn/at.packages.frontend/pull-requests/14)
79
+ - Sprint4/sangndd/feature/icons [`#12`](https://bitbucket.org/git-vn/at.packages.frontend/pull-requests/12)
80
+ - Sprint4/sangndd/feature/icons [`#11`](https://bitbucket.org/git-vn/at.packages.frontend/pull-requests/11)
81
+ - Sprint4/thanghn/fixbug/pivot alias border [`#10`](https://bitbucket.org/git-vn/at.packages.frontend/pull-requests/10)
82
+ - fix scroll dimension and total column [`#9`](https://bitbucket.org/git-vn/at.packages.frontend/pull-requests/9)
83
+ - [ANTALYSER][Filter][Khi filter filed có type date và semantic là "Month day" thì không load được danh sách các ngày trong tháng] [`#7`](https://bitbucket.org/git-vn/at.packages.frontend/pull-requests/7)
84
+ - [ANTALYSER][Pivot chart][Tính năng Add border shadow chưa hoạt động] [`#8`](https://bitbucket.org/git-vn/at.packages.frontend/pull-requests/8)
85
+ - Fix bug [ANTALYSER][Scatter chart][Label ở cột X và Y chưa đổi theo alias name đã change ở metric] [`#6`](https://bitbucket.org/git-vn/at.packages.frontend/pull-requests/6)
86
+ - Fix bug [Pivot chart] Row color không hiển thị được khi chọn hiển thị data của metric là Bar [`#5`](https://bitbucket.org/git-vn/at.packages.frontend/pull-requests/5)
87
+ - Fix bug package antscorp/charts [`#1`](https://bitbucket.org/git-vn/at.packages.frontend/pull-requests/1)
88
+ - Fix bug package antscorp/chart [`#2`](https://bitbucket.org/git-vn/at.packages.frontend/pull-requests/2)
89
+ - table component [`#1`](https://bitbucket.org/git-vn/at.packages.frontend/pull-requests/1)
90
+ - Update map indonesia [`fc94208`](https://bitbucket.org/git-vn/at.packages.frontend/commits/fc9420838b3f4087a5763017e9bf7e72ee8c5e31)
91
+ - Update geomap [`43f3098`](https://bitbucket.org/git-vn/at.packages.frontend/commits/43f3098506f30a215dc2b30734590e5c55f8b490)
92
+ - sync package master [`7476118`](https://bitbucket.org/git-vn/at.packages.frontend/commits/7476118de2fcfa1bd6b0a9e3c0f887b29fd149b6)
@@ -10,7 +10,7 @@ import { Spin, Tooltip, Icon, Button, EditableName, SelectAccount, QuillEditor,
10
10
  import Service from './Service';
11
11
  import { MESSAGE_TYPE, TICKET_CUSTOM_MESSAGE_KEY } from './constant';
12
12
  import { WrapperContent, WrapperContentInput, WrapperEditor, WrapperIconEditor, WrapperInputFile, WrapperLable, WrapperLeftContent, WrapperLinkItemFiles, WrapperMessageContent, WrapperRightContent, WrapperTextEdit, WrapperTextInput, DrawerHeader, } from './styled';
13
- import { formatAccountId, formatDatarender, formatParams, handleValidateContent, postCustomEvent, } from './util';
13
+ import { formatAccountId, formatDatarender, formatParams, handleValidateContent, postCustomEvent, uploadEmbeddedImages, } from './util';
14
14
  import { TCallBack } from '../../molecules/QuillEditor/QuillEditor';
15
15
  import { isBoolean } from 'lodash';
16
16
  const initValueInput = {
@@ -49,6 +49,7 @@ const Content = ({ portalId, token, action, ticketId, listUsers, domainTicket, d
49
49
  isError: false,
50
50
  message: '',
51
51
  });
52
+ const [embeddedImageError, setEmbeddedImageError] = useState('');
52
53
  const isUpdate = action === 'edit' && ticketId;
53
54
  const valueInputRef = useRef(valueInput);
54
55
  valueInputRef.current = valueInput;
@@ -94,20 +95,54 @@ const Content = ({ portalId, token, action, ticketId, listUsers, domainTicket, d
94
95
  setIsLoading(false);
95
96
  });
96
97
  };
97
- const handleSubmit = () => {
98
+ const uploadImagesBeforeSubmit = async (message) => {
99
+ setIsLoadingUpload(true);
100
+ setEmbeddedImageError('');
101
+ try {
102
+ const uploadedMessage = await uploadEmbeddedImages(message, Service.tickets.callApi.uploadImg({
103
+ domain: domainUpload,
104
+ token,
105
+ userId,
106
+ }));
107
+ if (uploadedMessage !== message) {
108
+ setTextValue(uploadedMessage);
109
+ updateValueInput({ message: uploadedMessage });
110
+ }
111
+ return uploadedMessage;
112
+ }
113
+ catch (error) {
114
+ setEmbeddedImageError(error instanceof Error && error.message === 'FILE_TOO_LARGE'
115
+ ? '*Maximum upload file size: 10MB'
116
+ : '*Image upload failed. Please try again.');
117
+ throw error;
118
+ }
119
+ finally {
120
+ setIsLoadingUpload(false);
121
+ }
122
+ };
123
+ const handleSubmit = async () => {
98
124
  const { title, ownerId, message } = valueInputRef.current;
99
125
  if (!title || !ownerId.length || !message) {
100
126
  setIsEmptyField(true);
101
127
  }
102
128
  else {
103
129
  if (!errFile.isError) {
104
- const params = formatParams(valueInputRef.current);
105
- const submitterEmail = listUsers?.find(user => user.userId === params.ownerId)?.email;
106
- fetchCreateTicket({
107
- ...params,
108
- submitterId: Number(userId),
109
- submitterEmail: submitterEmail,
110
- });
130
+ try {
131
+ const uploadedMessage = await uploadImagesBeforeSubmit(message);
132
+ const params = formatParams({
133
+ ...valueInputRef.current,
134
+ message: uploadedMessage,
135
+ });
136
+ const submitterEmail = listUsers?.find(user => user.userId === params.ownerId)?.email;
137
+ fetchCreateTicket({
138
+ ...params,
139
+ submitterId: Number(userId),
140
+ submitterEmail,
141
+ });
142
+ }
143
+ catch (error) {
144
+ // The upload error is shown below the editor.
145
+ }
111
146
  }
112
147
  // handleSendData(valueInput)
113
148
  }
@@ -231,6 +266,7 @@ const Content = ({ portalId, token, action, ticketId, listUsers, domainTicket, d
231
266
  }
232
267
  }, [isUpdate, openDrawer]);
233
268
  const handleEditorChange = content => {
269
+ setEmbeddedImageError('');
234
270
  setTextValue(content);
235
271
  updateValueInput({ message: content });
236
272
  // setIsLoadingUpload(content.includes('<img src="data:image/png;base64'));
@@ -244,32 +280,34 @@ const Content = ({ portalId, token, action, ticketId, listUsers, domainTicket, d
244
280
  }
245
281
  fetchUpdateFollwers(params);
246
282
  };
247
- const handleUpdateComment = () => {
248
- setIsLoading(true);
249
- const params = {
250
- followers: [],
251
- submitterId: null,
252
- ownerId: null,
253
- submitterEmail: '',
254
- properties: {},
255
- message: '',
256
- attachmentsZendesk: [],
257
- };
258
- if (valueInput?.followers?.length) {
259
- params.followers = formatAccountId(valueInput?.followers);
260
- }
261
- if (valueInput?.files?.length > 0) {
262
- params.properties = {
263
- attachments: valueInput?.files,
283
+ const handleUpdateComment = async () => {
284
+ const currentValueInput = valueInputRef.current;
285
+ try {
286
+ const uploadedMessage = await uploadImagesBeforeSubmit(currentValueInput.message);
287
+ const params = {
288
+ followers: [],
289
+ submitterId: userId,
290
+ ownerId: Number(currentValueInput?.ownerId[0]?.userId),
291
+ submitterEmail: listUsers?.find(user => +user.userId === +userId)?.email,
292
+ properties: {},
293
+ message: uploadedMessage,
294
+ attachmentsZendesk: currentValueInput.fileZendesk.map(file => file.token),
264
295
  };
296
+ if (currentValueInput?.followers?.length) {
297
+ params.followers = formatAccountId(currentValueInput.followers);
298
+ }
299
+ if (currentValueInput?.files?.length > 0) {
300
+ params.properties = {
301
+ attachments: currentValueInput.files,
302
+ };
303
+ }
304
+ setIsLoading(true);
305
+ fetchUpdateComment(params);
306
+ handleUpdateFollowers(currentValueInput?.followers?.map((fol) => fol.userId));
307
+ }
308
+ catch (error) {
309
+ // The upload error is shown below the editor.
265
310
  }
266
- params.submitterId = userId;
267
- params.submitterEmail = listUsers?.find(user => +user.userId === +userId)?.email;
268
- params.ownerId = Number(valueInput?.ownerId[0]?.userId);
269
- params.message = valueInput?.message;
270
- params.attachmentsZendesk = valueInput.fileZendesk.map(file => file.token);
271
- fetchUpdateComment(params);
272
- handleUpdateFollowers(valueInput?.followers?.map((fol) => fol.userId));
273
311
  };
274
312
  const handleUploadFile = async (file) => {
275
313
  setIsLoading(true);
@@ -387,7 +425,8 @@ const Content = ({ portalId, token, action, ticketId, listUsers, domainTicket, d
387
425
  isLoadingDetails ||
388
426
  isLoadingDataSouce ||
389
427
  isLoadingFollower ||
390
- isLoadingSelectedAccount, children: [_jsxs(DrawerHeader, { children: [_jsx(EditableName, { value: valueInput.title, onChange: handleOnchangeTitle, readonly: isUpdate, error: isEmptyField && (!valueInput.title || !valueInput.title.trim())
428
+ isLoadingSelectedAccount ||
429
+ isLoadingUpload, children: [_jsxs(DrawerHeader, { children: [_jsx(EditableName, { value: valueInput.title, onChange: handleOnchangeTitle, readonly: isUpdate, error: isEmptyField && (!valueInput.title || !valueInput.title.trim())
391
430
  ? "Title can't be empty"
392
431
  : '' }), _jsx("div", { id: "header-journey-right", className: "right-content", children: !isUpdate && (_jsx(Button, { disabled: isLoadingUpload, type: "primary", onClick: handleSubmit, children: "Save" })) })] }), _jsxs(WrapperContent, { style: { height: '100%' }, children: [_jsxs(WrapperLeftContent, { children: [_jsxs(WrapperContentInput, { style: { alignItems: 'flex-start' }, children: [_jsxs(WrapperLable, { children: ["Owner", _jsx("span", { style: { color: '#ff0000' }, children: "*" })] }), _jsxs("div", { children: [_jsx(SelectAccount, { type: "default", initData: (valueInput?.ownerId || []).map((item) => item.userId), nameKey: "userName", userIdKey: "userId", users: listUsers, isViewMode: isUpdate || !isAllAccount || accountEdit !== 4, onChange: (_, users) => updateValueInput({ ownerId: users }), onlyOne: true }), isEmptyField && !valueInput?.ownerId?.length && (_jsx("div", { className: "error-message", children: "*This field can't be empty" }))] })] }), _jsxs(WrapperContentInput, { style: { marginTop: '15px', alignItems: 'flex-start' }, children: [_jsx(WrapperLable, { children: "Follower(s)" }), _jsx(SelectAccount, { type: "default", initData: [
393
432
  ...accountManage,
@@ -404,7 +443,7 @@ const Content = ({ portalId, token, action, ticketId, listUsers, domainTicket, d
404
443
  background: `${!handleValidateContent(textValue) || isLoadingUpload
405
444
  ? '#ccc'
406
445
  : '#1f5fac'}`,
407
- }, onClick: handleUpdateComment, children: "Reply" }))] })] })] }), errFile.isError ? (_jsx("div", { className: "error-message", children: errFile.message })) : (isEmptyField &&
446
+ }, onClick: handleUpdateComment, children: "Reply" }))] })] })] }), embeddedImageError ? (_jsx("div", { className: "error-message", children: embeddedImageError })) : errFile.isError ? (_jsx("div", { className: "error-message", children: errFile.message })) : (isEmptyField &&
408
447
  !valueInput?.message && (_jsx("div", { className: "error-message", children: "*This field can't be empty" })))] }), isUpdate && (_jsx(Spin, { spinning: isLoadingComment, children: _jsx(WrapperMessageContent, { children: listComment?.map(comment => (_jsx(MessageComponent, { toUser: comment?.toUser, fromUser: comment?.fromUser, followers: comment?.followers, message: comment?.message, date: comment?.createdDate, attachments: comment?.attachments, timeZone: timeZone, submitterEmail: comment?.submitterEmail }, comment?.id))) }) }))] })] })] })] }));
409
448
  };
410
449
  export default Content;
@@ -27,6 +27,7 @@ export declare function getEntriesV2(res: any, def: any): {
27
27
  messageAPI?: undefined;
28
28
  };
29
29
  export declare const formatAccountId: (data: any) => any;
30
+ export { dataUrlToFile, uploadEmbeddedImages } from '../../../utils/uploadEmbeddedImages';
30
31
  export declare const formatParams: (data: any) => {
31
32
  title: string;
32
33
  ownerId: undefined;
@@ -294,6 +294,7 @@ export const formatAccountId = data => {
294
294
  const results = data?.map(item => item?.userId);
295
295
  return results;
296
296
  };
297
+ export { dataUrlToFile, uploadEmbeddedImages } from '../../../utils/uploadEmbeddedImages';
297
298
  export const formatParams = data => {
298
299
  const params = {
299
300
  title: '',
@@ -8,7 +8,7 @@ import MessageComponent from './components/MessageComponent';
8
8
  import { Spin, Tooltip, Icon, Button, SelectAccount, QuillEditor, } from '@antscorp/antsomi-ui/es/components';
9
9
  import Service from './Service';
10
10
  import { WrapperContent, WrapperContentInput, WrapperEditor, WrapperIconEditor, WrapperInputFile, WrapperLable, WrapperLeftContent, WrapperLinkItemFiles, WrapperMessageContent, WrapperRightContent, WrapperTextEdit, WrapperTextInput, } from './styled';
11
- import { formatDatarender, handleValidateContent } from './util';
11
+ import { formatDatarender, handleValidateContent, uploadEmbeddedImages, } from './util';
12
12
  import { get, keyBy } from 'lodash';
13
13
  import { TCallBack } from '../../molecules/QuillEditor/QuillEditor';
14
14
  const initValueInput = {
@@ -44,6 +44,7 @@ const Content = ({ apiKey, domain, portalId, token, action, ticketId, listUsers,
44
44
  isError: false,
45
45
  message: '',
46
46
  });
47
+ const [embeddedImageError, setEmbeddedImageError] = useState('');
47
48
  const isUpdate = action === 'edit' && ticketId;
48
49
  const valueInputRef = useRef(valueInput);
49
50
  valueInputRef.current = valueInput;
@@ -136,6 +137,7 @@ const Content = ({ apiKey, domain, portalId, token, action, ticketId, listUsers,
136
137
  }, 1000);
137
138
  }, []);
138
139
  const handleEditorChange = content => {
140
+ setEmbeddedImageError('');
139
141
  setTextValue(content);
140
142
  updateValueInput({ message: content });
141
143
  // setIsLoadingUpload(content.includes('<img src="data:image/png;base64'));
@@ -147,33 +149,61 @@ const Content = ({ apiKey, domain, portalId, token, action, ticketId, listUsers,
147
149
  params.followers = arrFolowers;
148
150
  fetchUpdateFollwers(params);
149
151
  };
150
- const handleUpdateComment = () => {
151
- setIsLoading(true);
152
- const params = {
153
- followers: [],
154
- submitterId: null,
155
- ownerId: null,
156
- message: '',
157
- properties: {
158
- attachments: [],
159
- },
160
- submitterEmail: '',
161
- attachmentsZendesk: [],
162
- };
163
- if (valueInput?.followers?.length) {
164
- params.followers = valueInput?.followers;
152
+ const uploadImagesBeforeSubmit = async (message) => {
153
+ setIsLoadingUpload(true);
154
+ setEmbeddedImageError('');
155
+ try {
156
+ const uploadedMessage = await uploadEmbeddedImages(message, Service.tickets.callApi.uploadFile({
157
+ domain: domainUpload,
158
+ }));
159
+ if (uploadedMessage !== message) {
160
+ setTextValue(uploadedMessage);
161
+ updateValueInput({ message: uploadedMessage });
162
+ }
163
+ return uploadedMessage;
164
+ }
165
+ catch (error) {
166
+ setEmbeddedImageError(error instanceof Error && error.message === 'FILE_TOO_LARGE'
167
+ ? '*Maximum upload file size: 10MB'
168
+ : '*Image upload failed. Please try again.');
169
+ throw error;
170
+ }
171
+ finally {
172
+ setIsLoadingUpload(false);
165
173
  }
166
- if (valueInput?.files?.length > 0) {
167
- params.properties.attachments = valueInput?.files;
174
+ };
175
+ const handleUpdateComment = async () => {
176
+ const currentValueInput = valueInputRef.current;
177
+ try {
178
+ const uploadedMessage = await uploadImagesBeforeSubmit(currentValueInput.message);
179
+ const params = {
180
+ followers: [],
181
+ submitterId: null,
182
+ ownerId: null,
183
+ message: uploadedMessage,
184
+ properties: {
185
+ attachments: [],
186
+ },
187
+ submitterEmail: '',
188
+ attachmentsZendesk: [],
189
+ };
190
+ if (currentValueInput?.followers?.length) {
191
+ params.followers = currentValueInput.followers;
192
+ }
193
+ if (currentValueInput?.files?.length > 0) {
194
+ params.properties.attachments = currentValueInput.files;
195
+ }
196
+ const mapUsers = keyBy(listUsers, 'email');
197
+ params.submitterId = +mapUsers[email]?.userId || 0;
198
+ params.submitterEmail = email;
199
+ params.attachmentsZendesk = currentValueInput.fileZendesk?.map(file => file?.token);
200
+ setIsLoading(true);
201
+ fetchUpdateComment(params);
202
+ handleUpdateFollowers(currentValueInput.followers?.map((fol) => fol.userId));
203
+ }
204
+ catch (error) {
205
+ // The upload error is shown below the editor.
168
206
  }
169
- const mapUsers = keyBy(listUsers, 'email');
170
- params.submitterId = +mapUsers[email]?.userId || 0;
171
- params.submitterEmail = email;
172
- params.ownerId = null;
173
- params.message = valueInput?.message;
174
- params.attachmentsZendesk = valueInput?.fileZendesk?.map(file => file?.token);
175
- fetchUpdateComment(params);
176
- handleUpdateFollowers(valueInput?.followers?.map((fol) => fol.userId));
177
207
  };
178
208
  const onChangeFollowers = arrFolowers => {
179
209
  updateValueInput({ followers: arrFolowers });
@@ -301,7 +331,11 @@ const Content = ({ apiKey, domain, portalId, token, action, ticketId, listUsers,
301
331
  break;
302
332
  }
303
333
  };
304
- return (_jsxs("div", { style: { height: '100%' }, children: [_jsxs(Helmet, { children: [_jsx("meta", { charSet: "utf-8" }), _jsx("title", { children: browserTitle })] }), _jsx(Spin, { style: { height: '100vh' }, spinning: isLoading || isLoadingDetails || isLoadingDataSouce || isLoadingFollower, children: _jsxs(WrapperContent, { style: { height: '100%' }, children: [_jsxs(WrapperLeftContent, { children: [_jsxs(WrapperContentInput, { style: { alignItems: 'flex-start' }, children: [_jsxs(WrapperLable, { children: ["Owner", _jsx("span", { style: { color: '#ff0000' }, children: "*" })] }), _jsx("div", { children: ownerEmail })] }), _jsxs(WrapperContentInput, { style: { marginTop: '15px', alignItems: 'flex-start' }, children: [_jsx(WrapperLable, { children: "Follower(s)" }), _jsx(SelectAccount, { type: "default", initData: ticketDetails?.followers || [], nameKey: "userName", userIdKey: "userId", users: listUsers, onChange: (_, followers) => onChangeFollowers(followers) })] }), dataSelects?.map((data) => _jsx(_Fragment, { children: renderOptions(data) })), _jsxs(WrapperContentInput, { style: { marginTop: '15px' }, children: [_jsx(WrapperLable, { htmlFor: "referenceUrl", children: "Reference URL" }), isUpdate ? (ticketDetails.referenceUrl ? (_jsx(Tooltip, { title: ticketDetails.referenceUrl, placement: "top", children: _jsx(WrapperTextEdit, { color: "#005fb8", href: ticketDetails.referenceUrl, target: "_blank", children: ticketDetails.referenceUrl }) })) : (_jsx(WrapperTextEdit, { color: "#005fb8", href: ticketDetails.referenceUrl, target: "_blank", children: ticketDetails.referenceUrl }))) : (_jsx(WrapperTextInput, { placeholder: "Reference URL", id: "referenceUrl",
334
+ return (_jsxs("div", { style: { height: '100%' }, children: [_jsxs(Helmet, { children: [_jsx("meta", { charSet: "utf-8" }), _jsx("title", { children: browserTitle })] }), _jsx(Spin, { style: { height: '100vh' }, spinning: isLoading ||
335
+ isLoadingDetails ||
336
+ isLoadingDataSouce ||
337
+ isLoadingFollower ||
338
+ isLoadingUpload, children: _jsxs(WrapperContent, { style: { height: '100%' }, children: [_jsxs(WrapperLeftContent, { children: [_jsxs(WrapperContentInput, { style: { alignItems: 'flex-start' }, children: [_jsxs(WrapperLable, { children: ["Owner", _jsx("span", { style: { color: '#ff0000' }, children: "*" })] }), _jsx("div", { children: ownerEmail })] }), _jsxs(WrapperContentInput, { style: { marginTop: '15px', alignItems: 'flex-start' }, children: [_jsx(WrapperLable, { children: "Follower(s)" }), _jsx(SelectAccount, { type: "default", initData: ticketDetails?.followers || [], nameKey: "userName", userIdKey: "userId", users: listUsers, onChange: (_, followers) => onChangeFollowers(followers) })] }), dataSelects?.map((data) => _jsx(_Fragment, { children: renderOptions(data) })), _jsxs(WrapperContentInput, { style: { marginTop: '15px' }, children: [_jsx(WrapperLable, { htmlFor: "referenceUrl", children: "Reference URL" }), isUpdate ? (ticketDetails.referenceUrl ? (_jsx(Tooltip, { title: ticketDetails.referenceUrl, placement: "top", children: _jsx(WrapperTextEdit, { color: "#005fb8", href: ticketDetails.referenceUrl, target: "_blank", children: ticketDetails.referenceUrl }) })) : (_jsx(WrapperTextEdit, { color: "#005fb8", href: ticketDetails.referenceUrl, target: "_blank", children: ticketDetails.referenceUrl }))) : (_jsx(WrapperTextInput, { placeholder: "Reference URL", id: "referenceUrl",
305
339
  // width="300px"
306
340
  onChange: handleOnchangeInput, name: "referenceUrl", value: valueInput.referenceUrl }))] })] }), _jsxs(WrapperRightContent, { children: [_jsxs(WrapperEditor, { children: [_jsxs(_Fragment, { children: [_jsx(QuillEditor, { value: textValue, uploadService: Service.tickets.callApi.uploadFile({
307
341
  domain: domainUpload,
@@ -309,7 +343,7 @@ const Content = ({ apiKey, domain, portalId, token, action, ticketId, listUsers,
309
343
  background: `${!handleValidateContent(textValue) || isLoadingUpload
310
344
  ? '#ccc'
311
345
  : '#1f5fac'}`,
312
- }, onClick: handleUpdateComment, children: "Reply" }))] })] })] }), errFile.isError ? (_jsx("div", { className: "error-message", children: errFile.message })) : (isEmptyField &&
346
+ }, onClick: handleUpdateComment, children: "Reply" }))] })] })] }), embeddedImageError ? (_jsx("div", { className: "error-message", children: embeddedImageError })) : errFile.isError ? (_jsx("div", { className: "error-message", children: errFile.message })) : (isEmptyField &&
313
347
  !valueInput?.message && (_jsx("div", { className: "error-message", children: "* This field can't be empty" })))] }), isUpdate && (_jsx(Spin, { spinning: isLoadingComment, children: _jsx(WrapperMessageContent, { children: listComment?.map(comment => (_jsx(MessageComponent, { toUser: comment?.toUser, fromUser: comment?.fromUser, followers: comment?.followers,
314
348
  // mailFollower={comment?.mailFollower}
315
349
  message: comment?.message, date: comment?.createdDate, attachments: comment?.attachments, timeZone: timeZone, submitterEmail: comment?.submitterEmail }, comment?.id))) }) }))] })] }) })] }));
@@ -28,6 +28,7 @@ export declare function getEntriesV2(res: any, def: any): {
28
28
  messageAPI?: undefined;
29
29
  };
30
30
  export declare const formatAccountId: (data: any) => any;
31
+ export { dataUrlToFile, uploadEmbeddedImages } from '../../../utils/uploadEmbeddedImages';
31
32
  export declare const formatParams: (data: any) => {
32
33
  title: string;
33
34
  ownerId: undefined;
@@ -287,6 +287,7 @@ export const formatAccountId = data => {
287
287
  const results = data?.map(item => item?.userId);
288
288
  return results;
289
289
  };
290
+ export { dataUrlToFile, uploadEmbeddedImages } from '../../../utils/uploadEmbeddedImages';
290
291
  export const formatParams = data => {
291
292
  const params = {
292
293
  title: '',
@@ -0,0 +1,7 @@
1
+ type TUploadEmbeddedImages = (params: {
2
+ files: File[];
3
+ mode: 'file';
4
+ }) => Promise<unknown>;
5
+ export declare const dataUrlToFile: (dataUrl: string, index: number) => File;
6
+ export declare const uploadEmbeddedImages: (message: string, uploadService: TUploadEmbeddedImages) => Promise<string>;
7
+ export {};
@@ -0,0 +1,65 @@
1
+ const DATA_IMAGE_PREFIX = /^\s*data:image\//i;
2
+ const DATA_IMAGE_URL = /^data:(image\/[^;,]+);base64,(.+)$/i;
3
+ const getUploadedImageUrl = (uploadedImage) => {
4
+ if (!uploadedImage || typeof uploadedImage !== 'object' || !('url' in uploadedImage)) {
5
+ return null;
6
+ }
7
+ const { url } = uploadedImage;
8
+ if (typeof url !== 'string' || !url.trim() || /^\s*data:/i.test(url)) {
9
+ return null;
10
+ }
11
+ try {
12
+ const parsedUrl = new URL(url.trim());
13
+ return parsedUrl.protocol === 'http:' || parsedUrl.protocol === 'https:'
14
+ ? parsedUrl.href
15
+ : null;
16
+ }
17
+ catch (error) {
18
+ return null;
19
+ }
20
+ };
21
+ export const dataUrlToFile = (dataUrl, index) => {
22
+ const matches = dataUrl.trim().match(DATA_IMAGE_URL);
23
+ if (!matches) {
24
+ throw new Error('INVALID_IMAGE_DATA');
25
+ }
26
+ const [, rawMimeType, base64] = matches;
27
+ const mimeType = rawMimeType.toLowerCase();
28
+ const padding = base64.endsWith('==') ? 2 : base64.endsWith('=') ? 1 : 0;
29
+ const fileSize = (base64.length * 3) / 4 - padding;
30
+ if (fileSize > 10 * 1024 * 1024) {
31
+ throw new Error('FILE_TOO_LARGE');
32
+ }
33
+ const binary = atob(base64);
34
+ const bytes = new Uint8Array(binary.length);
35
+ for (let byteIndex = 0; byteIndex < binary.length; byteIndex += 1) {
36
+ bytes[byteIndex] = binary.charCodeAt(byteIndex);
37
+ }
38
+ const extension = mimeType.split('/')[1].replace('jpeg', 'jpg');
39
+ return new File([bytes], `ticket-image-${index + 1}.${extension}`, { type: mimeType });
40
+ };
41
+ export const uploadEmbeddedImages = async (message, uploadService) => {
42
+ const container = document.createElement('div');
43
+ container.innerHTML = message;
44
+ const images = Array.from(container.querySelectorAll('img')).filter(image => DATA_IMAGE_PREFIX.test(image.getAttribute('src') || ''));
45
+ if (!images.length) {
46
+ return message;
47
+ }
48
+ const files = images.map((image, index) => dataUrlToFile(image.getAttribute('src') || '', index));
49
+ const response = await uploadService({ files, mode: 'file' });
50
+ const uploadedImages = response && typeof response === 'object' && 'data' in response && Array.isArray(response.data)
51
+ ? response.data
52
+ : [];
53
+ const uploadedUrls = uploadedImages.map(getUploadedImageUrl);
54
+ if (uploadedUrls.length !== images.length || uploadedUrls.some(uploadedUrl => !uploadedUrl)) {
55
+ throw new Error('UPLOAD_FAILED');
56
+ }
57
+ images.forEach((image, index) => {
58
+ image.setAttribute('src', uploadedUrls[index]);
59
+ });
60
+ const hasEmbeddedImage = Array.from(container.querySelectorAll('img')).some(image => DATA_IMAGE_PREFIX.test(image.getAttribute('src') || ''));
61
+ if (hasEmbeddedImage) {
62
+ throw new Error('UPLOAD_FAILED');
63
+ }
64
+ return container.innerHTML;
65
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@antscorp/antsomi-ui",
3
- "version": "2.0.121",
3
+ "version": "2.0.122",
4
4
  "description": "An enterprise-class UI design language and React UI library.",
5
5
  "sideEffects": [
6
6
  "dist/*",