@stack-spot/portal-components 2.27.0 → 2.27.2

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 (250) hide show
  1. package/CHANGELOG.md +635 -621
  2. package/dist/components/AnimatedHeight.d.ts +1 -1
  3. package/dist/components/AnimatedHeight.js +26 -26
  4. package/dist/components/AsyncContent.d.ts +1 -1
  5. package/dist/components/AsyncContent.js +1 -1
  6. package/dist/components/BannerWarning.d.ts +1 -1
  7. package/dist/components/BannerWarning.js +1 -1
  8. package/dist/components/Breadcrumb/index.d.ts +2 -2
  9. package/dist/components/Breadcrumb/index.js +1 -1
  10. package/dist/components/Breadcrumb/styled.js +31 -31
  11. package/dist/components/ButtonLoading.d.ts +1 -1
  12. package/dist/components/ButtonLoading.js +1 -1
  13. package/dist/components/ChatBot.d.ts +1 -1
  14. package/dist/components/ChatBot.js +1 -1
  15. package/dist/components/ContentValidateFilter.d.ts +1 -1
  16. package/dist/components/ContentValidateFilter.js +1 -1
  17. package/dist/components/FadingOverflow.d.ts +1 -1
  18. package/dist/components/FadingOverflow.js +69 -69
  19. package/dist/components/FileTreeView/More.d.ts +1 -1
  20. package/dist/components/FileTreeView/More.js +1 -1
  21. package/dist/components/FileTreeView/index.d.ts +1 -1
  22. package/dist/components/FileTreeView/index.js +1 -1
  23. package/dist/components/InfiniteScroll.d.ts +1 -1
  24. package/dist/components/InfiniteScroll.js +1 -1
  25. package/dist/components/InfoMaintenanceBanner.d.ts +1 -1
  26. package/dist/components/InfoMaintenanceBanner.js +2 -2
  27. package/dist/components/LazyMarkdown/BlockquoteMd.d.ts +1 -1
  28. package/dist/components/LazyMarkdown/BlockquoteMd.js +1 -1
  29. package/dist/components/LazyMarkdown/CodeViewer.d.ts +1 -1
  30. package/dist/components/LazyMarkdown/CodeViewer.js +76 -76
  31. package/dist/components/LazyMarkdown/Markdown.d.ts +1 -1
  32. package/dist/components/LazyMarkdown/Markdown.js +1 -1
  33. package/dist/components/LazyMarkdown/MarkdownButton.d.ts +1 -1
  34. package/dist/components/LazyMarkdown/MarkdownButton.js +1 -1
  35. package/dist/components/LazyMarkdown/Video.d.ts +1 -1
  36. package/dist/components/LazyMarkdown/Video.js +1 -1
  37. package/dist/components/LazyMarkdown/index.d.ts +1 -1
  38. package/dist/components/LazyMarkdown/index.js +1 -1
  39. package/dist/components/Placeholder.d.ts +7 -3
  40. package/dist/components/Placeholder.d.ts.map +1 -1
  41. package/dist/components/Placeholder.js +3 -3
  42. package/dist/components/Placeholder.js.map +1 -1
  43. package/dist/components/ScrollView.js +16 -16
  44. package/dist/components/Select/BadgeItem.d.ts +1 -1
  45. package/dist/components/Select/BadgeItem.js +1 -1
  46. package/dist/components/Select/ClearInput.d.ts +1 -1
  47. package/dist/components/Select/ClearInput.js +1 -1
  48. package/dist/components/Select/CloseItem.d.ts +1 -1
  49. package/dist/components/Select/CloseItem.js +1 -1
  50. package/dist/components/Select/CreatableSelect.js +1 -1
  51. package/dist/components/Select/CustomMenu.d.ts +1 -1
  52. package/dist/components/Select/CustomMenu.js +1 -1
  53. package/dist/components/Select/LabelItem.d.ts +1 -1
  54. package/dist/components/Select/LabelItem.js +1 -1
  55. package/dist/components/Select/MultiValue.d.ts +1 -1
  56. package/dist/components/Select/MultiValue.js +1 -1
  57. package/dist/components/Select/SelectInfiniteScroll.d.ts +1 -1
  58. package/dist/components/Select/SelectInfiniteScroll.js +1 -1
  59. package/dist/components/Select/SelectSearch.d.ts +1 -1
  60. package/dist/components/Select/SelectSearch.js +1 -1
  61. package/dist/components/SelectionList.d.ts +1 -1
  62. package/dist/components/SelectionList.js +61 -61
  63. package/dist/components/StatusCircle.d.ts +1 -1
  64. package/dist/components/StatusCircle.js +6 -6
  65. package/dist/components/Stepper/Navigation.js +4 -4
  66. package/dist/components/Stepper/Step.js +3 -3
  67. package/dist/components/Stepper/Stepper.js +6 -6
  68. package/dist/components/Stepper/headers.js +22 -22
  69. package/dist/components/Table/HeaderItem.js +1 -1
  70. package/dist/components/Table/SettingsVerticalMenu.d.ts +1 -1
  71. package/dist/components/Table/SettingsVerticalMenu.js +1 -1
  72. package/dist/components/Table/StyledLinkTable.d.ts +1 -1
  73. package/dist/components/Table/StyledLinkTable.js +5 -5
  74. package/dist/components/Table/TableData.d.ts +1 -1
  75. package/dist/components/Table/TableData.js +25 -25
  76. package/dist/components/TimelineSection.d.ts +1 -1
  77. package/dist/components/TimelineSection.js +14 -14
  78. package/dist/components/error/ErrorFeedback.d.ts +1 -1
  79. package/dist/components/error/ErrorFeedback.js +35 -35
  80. package/dist/components/error/NotFound.d.ts +1 -1
  81. package/dist/components/error/NotFound.js +1 -1
  82. package/dist/components/error/UnderMaintenance.d.ts +1 -1
  83. package/dist/components/error/UnderMaintenance.js +1 -1
  84. package/dist/components/form/Form/Form.d.ts +1 -1
  85. package/dist/components/form/Form/Form.js +1 -1
  86. package/dist/components/form/Form/FormGroup.d.ts +2 -2
  87. package/dist/components/form/Form/FormGroup.js +1 -1
  88. package/dist/components/form/SearchInput.d.ts +1 -1
  89. package/dist/components/form/SearchInput.js +1 -1
  90. package/dist/components/form/Select/CustomSelect.d.ts +1 -1
  91. package/dist/components/form/Select/CustomSelect.js +1 -1
  92. package/dist/components/form/Select/DetailedSelect.d.ts +1 -1
  93. package/dist/components/form/Select/DetailedSelect.js +1 -1
  94. package/dist/components/form/Select/Select.d.ts +1 -1
  95. package/dist/components/form/Select/Select.js +1 -1
  96. package/dist/components/form/Select/styled.js +161 -161
  97. package/dist/components/form/Select/utils.js +1 -1
  98. package/dist/components/notification/NotificationComponent.d.ts +1 -1
  99. package/dist/components/notification/NotificationComponent.js +54 -54
  100. package/dist/components/notification/NotificationItem.d.ts +1 -1
  101. package/dist/components/notification/NotificationItem.d.ts.map +1 -1
  102. package/dist/components/notification/NotificationItem.js +11 -5
  103. package/dist/components/notification/NotificationItem.js.map +1 -1
  104. package/dist/components/notification/NotificationList.d.ts +1 -1
  105. package/dist/components/notification/NotificationList.d.ts.map +1 -1
  106. package/dist/components/notification/NotificationList.js +44 -44
  107. package/dist/components/notification/NotificationList.js.map +1 -1
  108. package/dist/components/notification/NotificationPlaceholder.d.ts +1 -1
  109. package/dist/components/notification/NotificationPlaceholder.d.ts.map +1 -1
  110. package/dist/components/notification/NotificationPlaceholder.js +2 -2
  111. package/dist/components/notification/NotificationPlaceholder.js.map +1 -1
  112. package/dist/containers/NotificationsPage.d.ts +1 -1
  113. package/dist/containers/NotificationsPage.d.ts.map +1 -1
  114. package/dist/containers/NotificationsPage.js +24 -11
  115. package/dist/containers/NotificationsPage.js.map +1 -1
  116. package/dist/context/anchor.d.ts +1 -1
  117. package/dist/context/anchor.js +1 -1
  118. package/dist/context/loading.d.ts +1 -1
  119. package/dist/context/loading.js +1 -1
  120. package/dist/context/notification/context.d.ts +1 -1
  121. package/dist/context/notification/context.js +1 -1
  122. package/dist/context/notification/types.d.ts +1 -0
  123. package/dist/context/notification/types.d.ts.map +1 -1
  124. package/dist/hooks/date.js +1 -1
  125. package/dist/hooks/service-now.js +28 -28
  126. package/dist/svg/AI.d.ts +1 -1
  127. package/dist/svg/AI.js +1 -1
  128. package/dist/svg/CS.d.ts +1 -1
  129. package/dist/svg/CS.js +1 -1
  130. package/dist/svg/EDP.d.ts +1 -1
  131. package/dist/svg/EDP.js +1 -1
  132. package/dist/svg/Forbidden.d.ts +1 -1
  133. package/dist/svg/Forbidden.js +1 -1
  134. package/dist/svg/GenericPlaceholder.d.ts +4 -2
  135. package/dist/svg/GenericPlaceholder.d.ts.map +1 -1
  136. package/dist/svg/GenericPlaceholder.js +2 -2
  137. package/dist/svg/GenericPlaceholder.js.map +1 -1
  138. package/dist/svg/HUB.d.ts +1 -1
  139. package/dist/svg/HUB.js +1 -1
  140. package/dist/svg/Logo.d.ts +1 -1
  141. package/dist/svg/Logo.js +1 -1
  142. package/dist/svg/MiniLogo.d.ts +1 -1
  143. package/dist/svg/MiniLogo.js +1 -1
  144. package/dist/svg/NotFound.d.ts +1 -1
  145. package/dist/svg/NotFound.js +1 -1
  146. package/dist/svg/ServerError.d.ts +1 -1
  147. package/dist/svg/ServerError.js +1 -1
  148. package/dist/svg/Unauthenticated.d.ts +1 -1
  149. package/dist/svg/Unauthenticated.js +1 -1
  150. package/package.json +6 -6
  151. package/readme.md +66 -66
  152. package/src/components/AnimatedHeight.tsx +174 -174
  153. package/src/components/AsyncContent.tsx +78 -78
  154. package/src/components/BannerWarning.tsx +91 -91
  155. package/src/components/Breadcrumb/index.tsx +76 -76
  156. package/src/components/Breadcrumb/styled.ts +37 -37
  157. package/src/components/ButtonLoading.tsx +29 -29
  158. package/src/components/ChatBot.tsx +82 -82
  159. package/src/components/ContentValidateFilter.tsx +15 -15
  160. package/src/components/FadingOverflow.tsx +265 -265
  161. package/src/components/FileTreeView/More.tsx +114 -114
  162. package/src/components/FileTreeView/index.tsx +186 -186
  163. package/src/components/InfiniteScroll.tsx +24 -24
  164. package/src/components/InfoMaintenanceBanner.tsx +29 -29
  165. package/src/components/LazyMarkdown/BlockquoteMd.tsx +107 -107
  166. package/src/components/LazyMarkdown/CodeViewer.tsx +161 -161
  167. package/src/components/LazyMarkdown/Markdown.tsx +122 -122
  168. package/src/components/LazyMarkdown/MarkdownButton.tsx +24 -24
  169. package/src/components/LazyMarkdown/Video.tsx +13 -13
  170. package/src/components/LazyMarkdown/index.tsx +21 -21
  171. package/src/components/Placeholder.tsx +123 -118
  172. package/src/components/ScrollView.tsx +57 -57
  173. package/src/components/Select/BadgeItem.tsx +58 -58
  174. package/src/components/Select/ClearInput.tsx +24 -24
  175. package/src/components/Select/CloseItem.tsx +38 -38
  176. package/src/components/Select/CreatableSelect.tsx +155 -155
  177. package/src/components/Select/CustomMenu.tsx +16 -16
  178. package/src/components/Select/LabelItem.tsx +8 -8
  179. package/src/components/Select/MultiValue.tsx +49 -49
  180. package/src/components/Select/SelectInfiniteScroll.tsx +82 -82
  181. package/src/components/Select/SelectSearch.tsx +195 -195
  182. package/src/components/Select/index.tsx +7 -7
  183. package/src/components/Select/types.ts +8 -8
  184. package/src/components/SelectionList.tsx +427 -427
  185. package/src/components/StatusCircle.tsx +67 -67
  186. package/src/components/Stepper/Navigation.tsx +97 -97
  187. package/src/components/Stepper/Step.tsx +30 -30
  188. package/src/components/Stepper/Stepper.tsx +113 -113
  189. package/src/components/Stepper/headers.tsx +64 -64
  190. package/src/components/Stepper/index.ts +3 -3
  191. package/src/components/Table/HeaderItem.tsx +52 -52
  192. package/src/components/Table/SettingsVerticalMenu.tsx +50 -50
  193. package/src/components/Table/StyledLinkTable.tsx +22 -22
  194. package/src/components/Table/TableData.tsx +251 -251
  195. package/src/components/Table/index.tsx +2 -2
  196. package/src/components/TimelineSection.tsx +66 -66
  197. package/src/components/error/ErrorFeedback.tsx +217 -217
  198. package/src/components/error/NotFound.tsx +24 -24
  199. package/src/components/error/UnderMaintenance.tsx +30 -30
  200. package/src/components/error/index.ts +4 -4
  201. package/src/components/form/Form/Form.tsx +101 -101
  202. package/src/components/form/Form/FormGroup.tsx +221 -221
  203. package/src/components/form/Form/index.ts +2 -2
  204. package/src/components/form/SearchInput.tsx +69 -69
  205. package/src/components/form/Select/CustomSelect.tsx +232 -232
  206. package/src/components/form/Select/DetailedSelect.tsx +85 -85
  207. package/src/components/form/Select/Select.tsx +67 -67
  208. package/src/components/form/Select/index.ts +4 -4
  209. package/src/components/form/Select/styled.ts +165 -165
  210. package/src/components/form/Select/types.ts +112 -112
  211. package/src/components/form/Select/utils.tsx +28 -28
  212. package/src/components/notification/NotificationComponent.tsx +340 -340
  213. package/src/components/notification/NotificationItem.tsx +345 -336
  214. package/src/components/notification/NotificationList.tsx +179 -178
  215. package/src/components/notification/NotificationPlaceholder.tsx +44 -43
  216. package/src/components/notification/types.ts +72 -72
  217. package/src/containers/NotificationsPage.tsx +119 -98
  218. package/src/context/anchor.tsx +37 -37
  219. package/src/context/loading.tsx +36 -36
  220. package/src/context/notification/LazyNotificationList.ts +103 -103
  221. package/src/context/notification/NotificationController.ts +104 -104
  222. package/src/context/notification/context.tsx +23 -23
  223. package/src/context/notification/hooks.ts +98 -98
  224. package/src/context/notification/types.ts +66 -65
  225. package/src/hooks/date.ts +31 -31
  226. package/src/hooks/keyboard.tsx +128 -128
  227. package/src/hooks/manual-render.tsx +10 -10
  228. package/src/hooks/service-now.tsx +233 -233
  229. package/src/hooks/text.tsx +30 -30
  230. package/src/hooks/title.tsx +28 -28
  231. package/src/hooks/use-effect-once.tsx +43 -43
  232. package/src/index.ts +19 -19
  233. package/src/notifications.ts +11 -11
  234. package/src/svg/AI.tsx +41 -41
  235. package/src/svg/CS.tsx +48 -48
  236. package/src/svg/EDP.tsx +31 -31
  237. package/src/svg/Forbidden.tsx +22 -22
  238. package/src/svg/GenericPlaceholder.tsx +20 -20
  239. package/src/svg/HUB.tsx +48 -48
  240. package/src/svg/Logo.tsx +16 -16
  241. package/src/svg/MiniLogo.tsx +12 -12
  242. package/src/svg/NotFound.tsx +16 -16
  243. package/src/svg/ServerError.tsx +33 -33
  244. package/src/svg/Unauthenticated.tsx +16 -16
  245. package/src/svg/index.ts +11 -11
  246. package/src/utils/accessibility.ts +135 -135
  247. package/src/utils/cookie.ts +73 -73
  248. package/src/utils/promise.ts +5 -5
  249. package/src/utils/read-file.ts +16 -16
  250. package/tsconfig.json +10 -10
@@ -1,104 +1,104 @@
1
- import { pull } from 'lodash'
2
- import { LazyNotificationList } from './LazyNotificationList'
3
- import { LoadNotificationsFilters, NotificationConfig, UnreadNotificationListener } from './types'
4
-
5
- const DEFAULT_POLLING_MS = 120000
6
-
7
- export class NotificationController {
8
- private lazyLists = new Map<number, LazyNotificationList>()
9
- readonly config: NotificationConfig
10
- private readStatusMap = new Map<string, boolean>()
11
- private nextId = 1
12
- private unreadNotification = false
13
- private lastUnreadVerification: Date | undefined
14
- private pollingTimeoutId: number | undefined
15
- private unreadNotificationListeners: UnreadNotificationListener[] = []
16
-
17
- constructor(config: NotificationConfig) {
18
- this.config = config
19
- config.pollingMS ??= DEFAULT_POLLING_MS
20
- config.notificationsPath ??= '/notifications'
21
- }
22
-
23
- private hasUnreadNotificationInMemory() {
24
- for (const [, read] of this.readStatusMap) {
25
- if (!read.valueOf()) return true
26
- }
27
- return false
28
- }
29
-
30
- private setUnreadNotification(value: boolean) {
31
- this.unreadNotification = value
32
- if (!value) {
33
- this.pollingTimeoutId = window.setTimeout(() => this.checkUnread(), this.config.pollingMS)
34
- }
35
- this.unreadNotificationListeners.forEach(l => l(value))
36
- }
37
-
38
- createLazyNotificationList(filters?: LoadNotificationsFilters): LazyNotificationList {
39
- const list = new LazyNotificationList({
40
- id: this.nextId++,
41
- filters,
42
- load: async (options) => {
43
- const result = await this.config.load(options)
44
- result.items.forEach((i) => {
45
- this.readStatusMap.set(i.id, i.committed)
46
- // updating the unread indicator, since it might be stale
47
- if (!i.committed) this.setUnreadNotification(true)
48
- })
49
- this.lazyLists.forEach(l => l !== list && list.update(this.readStatusMap))
50
- return result
51
- },
52
- })
53
- this.lazyLists.set(list.id, list)
54
- return list
55
- }
56
-
57
- destroyLazyNotificationList(id: number) {
58
- this.lazyLists.get(id)?.mute()
59
- this.lazyLists.delete(id)
60
- }
61
-
62
- async checkUnread() {
63
- window.clearInterval(this.pollingTimeoutId)
64
- this.setUnreadNotification(await this.config.checkForUnreadNotificationsSince(this.lastUnreadVerification))
65
- this.lastUnreadVerification = new Date()
66
- }
67
-
68
- async markAsCommitted(notificationId: string) {
69
- if (this.readStatusMap.get(notificationId)) return
70
- this.readStatusMap.set(notificationId, true)
71
- this.lazyLists.forEach(l => l.update(this.readStatusMap))
72
- try {
73
- await this.config.markAsCommitted(notificationId)
74
- // update the notification indicator: this is an inconsistency, we should actually ask the API, but since it's an expensive
75
- // operation in the backend, we won't, instead, we'll only check the notifications we have loaded in memory.
76
- if (!this.hasUnreadNotificationInMemory()) this.setUnreadNotification(false)
77
- } catch {
78
- this.readStatusMap.set(notificationId, false)
79
- this.lazyLists.forEach(l => l.update(this.readStatusMap))
80
- }
81
- }
82
-
83
- onUnreadNotificationChange(listener: UnreadNotificationListener) {
84
- this.unreadNotificationListeners.push(listener)
85
- return () => {
86
- pull(this.unreadNotificationListeners, listener)
87
- }
88
- }
89
-
90
- hasUnreadNotification() {
91
- return this.unreadNotification
92
- }
93
-
94
- reset() {
95
- this.lazyLists.clear()
96
- this.readStatusMap.clear()
97
- this.lastUnreadVerification = undefined
98
- this.unreadNotification = false
99
- }
100
-
101
- mute() {
102
- this.unreadNotificationListeners = []
103
- }
104
- }
1
+ import { pull } from 'lodash'
2
+ import { LazyNotificationList } from './LazyNotificationList'
3
+ import { LoadNotificationsFilters, NotificationConfig, UnreadNotificationListener } from './types'
4
+
5
+ const DEFAULT_POLLING_MS = 120000
6
+
7
+ export class NotificationController {
8
+ private lazyLists = new Map<number, LazyNotificationList>()
9
+ readonly config: NotificationConfig
10
+ private readStatusMap = new Map<string, boolean>()
11
+ private nextId = 1
12
+ private unreadNotification = false
13
+ private lastUnreadVerification: Date | undefined
14
+ private pollingTimeoutId: number | undefined
15
+ private unreadNotificationListeners: UnreadNotificationListener[] = []
16
+
17
+ constructor(config: NotificationConfig) {
18
+ this.config = config
19
+ config.pollingMS ??= DEFAULT_POLLING_MS
20
+ config.notificationsPath ??= '/notifications'
21
+ }
22
+
23
+ private hasUnreadNotificationInMemory() {
24
+ for (const [, read] of this.readStatusMap) {
25
+ if (!read.valueOf()) return true
26
+ }
27
+ return false
28
+ }
29
+
30
+ private setUnreadNotification(value: boolean) {
31
+ this.unreadNotification = value
32
+ if (!value) {
33
+ this.pollingTimeoutId = window.setTimeout(() => this.checkUnread(), this.config.pollingMS)
34
+ }
35
+ this.unreadNotificationListeners.forEach(l => l(value))
36
+ }
37
+
38
+ createLazyNotificationList(filters?: LoadNotificationsFilters): LazyNotificationList {
39
+ const list = new LazyNotificationList({
40
+ id: this.nextId++,
41
+ filters,
42
+ load: async (options) => {
43
+ const result = await this.config.load(options)
44
+ result.items.forEach((i) => {
45
+ this.readStatusMap.set(i.id, i.committed)
46
+ // updating the unread indicator, since it might be stale
47
+ if (!i.committed) this.setUnreadNotification(true)
48
+ })
49
+ this.lazyLists.forEach(l => l !== list && list.update(this.readStatusMap))
50
+ return result
51
+ },
52
+ })
53
+ this.lazyLists.set(list.id, list)
54
+ return list
55
+ }
56
+
57
+ destroyLazyNotificationList(id: number) {
58
+ this.lazyLists.get(id)?.mute()
59
+ this.lazyLists.delete(id)
60
+ }
61
+
62
+ async checkUnread() {
63
+ window.clearInterval(this.pollingTimeoutId)
64
+ this.setUnreadNotification(await this.config.checkForUnreadNotificationsSince(this.lastUnreadVerification))
65
+ this.lastUnreadVerification = new Date()
66
+ }
67
+
68
+ async markAsCommitted(notificationId: string) {
69
+ if (this.readStatusMap.get(notificationId)) return
70
+ this.readStatusMap.set(notificationId, true)
71
+ this.lazyLists.forEach(l => l.update(this.readStatusMap))
72
+ try {
73
+ await this.config.markAsCommitted(notificationId)
74
+ // update the notification indicator: this is an inconsistency, we should actually ask the API, but since it's an expensive
75
+ // operation in the backend, we won't, instead, we'll only check the notifications we have loaded in memory.
76
+ if (!this.hasUnreadNotificationInMemory()) this.setUnreadNotification(false)
77
+ } catch {
78
+ this.readStatusMap.set(notificationId, false)
79
+ this.lazyLists.forEach(l => l.update(this.readStatusMap))
80
+ }
81
+ }
82
+
83
+ onUnreadNotificationChange(listener: UnreadNotificationListener) {
84
+ this.unreadNotificationListeners.push(listener)
85
+ return () => {
86
+ pull(this.unreadNotificationListeners, listener)
87
+ }
88
+ }
89
+
90
+ hasUnreadNotification() {
91
+ return this.unreadNotification
92
+ }
93
+
94
+ reset() {
95
+ this.lazyLists.clear()
96
+ this.readStatusMap.clear()
97
+ this.lastUnreadVerification = undefined
98
+ this.unreadNotification = false
99
+ }
100
+
101
+ mute() {
102
+ this.unreadNotificationListeners = []
103
+ }
104
+ }
@@ -1,23 +1,23 @@
1
- import { createContext, useContext } from 'react'
2
- import { NotificationController } from './NotificationController'
3
-
4
- export interface Props {
5
- children: React.ReactNode,
6
- controller: NotificationController,
7
- }
8
-
9
- const context = createContext<NotificationController | undefined>(undefined)
10
-
11
- export const NotificationProvider = ({ children, controller }: Props) => (
12
- <context.Provider value={controller}>{children}</context.Provider>
13
- )
14
-
15
- export function useNotificationController() {
16
- const controller = useContext(context)
17
- if (!controller) {
18
- throw new Error(
19
- 'A NotificationController was requested, but no NotificationController is available in the React Context. Please, be sure to wrap your component in a NotificationProvider.',
20
- )
21
- }
22
- return controller
23
- }
1
+ import { createContext, useContext } from 'react'
2
+ import { NotificationController } from './NotificationController'
3
+
4
+ export interface Props {
5
+ children: React.ReactNode,
6
+ controller: NotificationController,
7
+ }
8
+
9
+ const context = createContext<NotificationController | undefined>(undefined)
10
+
11
+ export const NotificationProvider = ({ children, controller }: Props) => (
12
+ <context.Provider value={controller}>{children}</context.Provider>
13
+ )
14
+
15
+ export function useNotificationController() {
16
+ const controller = useContext(context)
17
+ if (!controller) {
18
+ throw new Error(
19
+ 'A NotificationController was requested, but no NotificationController is available in the React Context. Please, be sure to wrap your component in a NotificationProvider.',
20
+ )
21
+ }
22
+ return controller
23
+ }
@@ -1,98 +1,98 @@
1
- import { useLanguage } from '@stack-spot/portal-translate'
2
- import { useCallback, useRef, useState } from 'react'
3
- import { useManualRender } from '../../hooks/manual-render'
4
- import { useEffectOnce } from '../../hooks/use-effect-once'
5
- import { GetTenantNotificationsResponse } from '../../notifications'
6
- import { useNotificationController } from './context'
7
- import { LazyNotificationList } from './LazyNotificationList'
8
- import { LoadNotificationsFilters } from './types'
9
-
10
- export function useNotificationList(initialFilters: LoadNotificationsFilters = {}) {
11
- const { repaint } = useManualRender()
12
- const [status, setStatus] = useState<'startup' | 'idle' | 'error' | 'loading'>('startup')
13
- const error = useRef<any>()
14
- const controller = useNotificationController()
15
- const [filters, setFilters] = useState(initialFilters)
16
- const list = useRef<LazyNotificationList | undefined>()
17
-
18
- useEffectOnce(() => {
19
- async function start() {
20
- list.current = controller.createLazyNotificationList(initialFilters)
21
- list.current.subscribe(repaint)
22
- try {
23
- await list.current?.loadMore()
24
- setStatus('idle')
25
- } catch (e) {
26
- setStatus('error')
27
- error.current = e
28
- }
29
- }
30
- start()
31
- return () => {
32
- if (list.current) controller.destroyLazyNotificationList(list.current.id)
33
- }
34
- })
35
-
36
- const applyFilters = useCallback(async (newFilters: LoadNotificationsFilters) => {
37
- if (!list.current) return
38
- setStatus('loading')
39
- // since we're inside a useCallback, we don't have direct access to the current value of "filters". The only way we can access this
40
- // value is from the function "setFilters". But "setFilters" run async. For this reason, we wait the function to run and retrieve the
41
- // current value before continuing, hence the use of the await and promise below.
42
- const { next, prev } = await new Promise<{ next: LoadNotificationsFilters, prev: LoadNotificationsFilters }>((resolve) => {
43
- setFilters((filters) => {
44
- const prev = filters
45
- const next = { ...filters, ...newFilters }
46
- resolve({ prev, next })
47
- return next
48
- })
49
- })
50
- try {
51
- await list.current.applyFilters(next)
52
- setStatus('idle')
53
- error.current = undefined
54
- } catch (e) {
55
- setFilters(prev)
56
- if (error.current) {
57
- error.current = e
58
- setStatus('error')
59
- } else {
60
- setStatus('idle')
61
- }
62
- }
63
- }, [])
64
-
65
- return {
66
- status,
67
- items: list.current?.items ?? [],
68
- hasMore: list.current?.hasMore() ?? false,
69
- loadMore: () => list.current?.loadMore(),
70
- refresh: () => applyFilters(filters),
71
- applyFilters,
72
- filters,
73
- error: error.current,
74
- }
75
- }
76
-
77
- export function useGetNotificationTitleAndDescription({ content }: GetTenantNotificationsResponse) {
78
- const language = useLanguage()
79
- const { title, description } = content?.[language] || content?.en || {}
80
-
81
- return {
82
- title,
83
- description,
84
- }
85
- }
86
-
87
- export function useUnreadNotifications() {
88
- const [hasUnreadNotifications, setUnreadNotifications] = useState(false)
89
- const controller = useNotificationController()
90
-
91
- useEffectOnce(() => {
92
- const unsubscribe = controller.onUnreadNotificationChange(setUnreadNotifications)
93
- controller.checkUnread()
94
- return unsubscribe
95
- })
96
-
97
- return hasUnreadNotifications
98
- }
1
+ import { useLanguage } from '@stack-spot/portal-translate'
2
+ import { useCallback, useRef, useState } from 'react'
3
+ import { useManualRender } from '../../hooks/manual-render'
4
+ import { useEffectOnce } from '../../hooks/use-effect-once'
5
+ import { GetTenantNotificationsResponse } from '../../notifications'
6
+ import { useNotificationController } from './context'
7
+ import { LazyNotificationList } from './LazyNotificationList'
8
+ import { LoadNotificationsFilters } from './types'
9
+
10
+ export function useNotificationList(initialFilters: LoadNotificationsFilters = {}) {
11
+ const { repaint } = useManualRender()
12
+ const [status, setStatus] = useState<'startup' | 'idle' | 'error' | 'loading'>('startup')
13
+ const error = useRef<any>()
14
+ const controller = useNotificationController()
15
+ const [filters, setFilters] = useState(initialFilters)
16
+ const list = useRef<LazyNotificationList | undefined>()
17
+
18
+ useEffectOnce(() => {
19
+ async function start() {
20
+ list.current = controller.createLazyNotificationList(initialFilters)
21
+ list.current.subscribe(repaint)
22
+ try {
23
+ await list.current?.loadMore()
24
+ setStatus('idle')
25
+ } catch (e) {
26
+ setStatus('error')
27
+ error.current = e
28
+ }
29
+ }
30
+ start()
31
+ return () => {
32
+ if (list.current) controller.destroyLazyNotificationList(list.current.id)
33
+ }
34
+ })
35
+
36
+ const applyFilters = useCallback(async (newFilters: LoadNotificationsFilters) => {
37
+ if (!list.current) return
38
+ setStatus('loading')
39
+ // since we're inside a useCallback, we don't have direct access to the current value of "filters". The only way we can access this
40
+ // value is from the function "setFilters". But "setFilters" run async. For this reason, we wait the function to run and retrieve the
41
+ // current value before continuing, hence the use of the await and promise below.
42
+ const { next, prev } = await new Promise<{ next: LoadNotificationsFilters, prev: LoadNotificationsFilters }>((resolve) => {
43
+ setFilters((filters) => {
44
+ const prev = filters
45
+ const next = { ...filters, ...newFilters }
46
+ resolve({ prev, next })
47
+ return next
48
+ })
49
+ })
50
+ try {
51
+ await list.current.applyFilters(next)
52
+ setStatus('idle')
53
+ error.current = undefined
54
+ } catch (e) {
55
+ setFilters(prev)
56
+ if (error.current) {
57
+ error.current = e
58
+ setStatus('error')
59
+ } else {
60
+ setStatus('idle')
61
+ }
62
+ }
63
+ }, [])
64
+
65
+ return {
66
+ status,
67
+ items: list.current?.items ?? [],
68
+ hasMore: list.current?.hasMore() ?? false,
69
+ loadMore: () => list.current?.loadMore(),
70
+ refresh: () => applyFilters(filters),
71
+ applyFilters,
72
+ filters,
73
+ error: error.current,
74
+ }
75
+ }
76
+
77
+ export function useGetNotificationTitleAndDescription({ content }: GetTenantNotificationsResponse) {
78
+ const language = useLanguage()
79
+ const { title, description } = content?.[language] || content?.en || {}
80
+
81
+ return {
82
+ title,
83
+ description,
84
+ }
85
+ }
86
+
87
+ export function useUnreadNotifications() {
88
+ const [hasUnreadNotifications, setUnreadNotifications] = useState(false)
89
+ const controller = useNotificationController()
90
+
91
+ useEffectOnce(() => {
92
+ const unsubscribe = controller.onUnreadNotificationChange(setUnreadNotifications)
93
+ controller.checkUnread()
94
+ return unsubscribe
95
+ })
96
+
97
+ return hasUnreadNotifications
98
+ }
@@ -1,65 +1,66 @@
1
- import { GetTenantNotificationsResponse, ResponseModelGetTenantNotificationsResponse } from '../../notifications'
2
-
3
- export type NotificationPriority = 'HIGH' | 'MEDIUM' | 'LOW'
4
-
5
- export type NotificationContext = 'ACCOUNT' | 'STUDIO' | 'WORKSPACE'
6
-
7
- export interface LoadNotificationsFilters {
8
- committed?: boolean,
9
- criticality?: NotificationPriority,
10
- search?: string,
11
- context?: NotificationContext,
12
- size?: number,
13
- isBanner?: boolean,
14
- }
15
-
16
- export interface LoadNotificationsOptions extends LoadNotificationsFilters {
17
- page: number,
18
- isBanner?: boolean,
19
- }
20
-
21
- export type LazyNotificationListener = (notifications: GetTenantNotificationsResponse[], hasMore: boolean) => void
22
-
23
- export interface NotificationConfig {
24
- /**
25
- * Fetches the notifications according to the options passed as parameters.
26
- * @param options page, size and filters.
27
- * @returns a promise that resolves to a {@link ResponseModelGetTenantNotificationsResponse}.
28
- */
29
- load(options: LoadNotificationsOptions): Promise<ResponseModelGetTenantNotificationsResponse>,
30
- /**
31
- * Check for unread notifications since the date passed as parameter.
32
- * @param date the date to start the search from
33
- * @returns a promise that resolves to true if there are unread notifications or false otherwise.
34
- */
35
- checkForUnreadNotificationsSince(date?: Date): Promise<boolean>,
36
- /**
37
- * Marks the notification with the id passed as parameter as read (committed).
38
- * @param id the id of the notification.
39
- */
40
- markAsCommitted(id: string): Promise<void>,
41
- /**
42
- * How long (ms) we should wait before checking for unread notifications again.
43
- * @default 120000
44
- */
45
- pollingMS?: number,
46
- /**
47
- * Path to the notifications page.
48
- * @default '/notifications'
49
- */
50
- notificationsPath?: string,
51
- /**
52
- * A function to call whenever an action button of a notification is clicked. It receives the id of the notification as a parameter.
53
- *
54
- * Useful for adding a behavior other than committing the notification and redirecting to its action. Example: analytics.
55
- *
56
- * @param id the id of the notification.
57
- */
58
- onClickAction?: (id: string) => void,
59
- /**
60
- * Function that is called whenever the "read more" button is clicked.
61
- */
62
- onClickViewNotification?: () => void,
63
- }
64
-
65
- export type UnreadNotificationListener = (hasUnreadNotification: boolean) => void
1
+ import { GetTenantNotificationsResponse, ResponseModelGetTenantNotificationsResponse } from '../../notifications'
2
+
3
+ export type NotificationPriority = 'HIGH' | 'MEDIUM' | 'LOW'
4
+
5
+ export type NotificationContext = 'ACCOUNT' | 'STUDIO' | 'WORKSPACE'
6
+
7
+ export interface LoadNotificationsFilters {
8
+ committed?: boolean,
9
+ criticality?: NotificationPriority,
10
+ search?: string,
11
+ context?: NotificationContext,
12
+ size?: number,
13
+ isBanner?: boolean,
14
+ createdSince?: number,
15
+ }
16
+
17
+ export interface LoadNotificationsOptions extends LoadNotificationsFilters {
18
+ page: number,
19
+ isBanner?: boolean,
20
+ }
21
+
22
+ export type LazyNotificationListener = (notifications: GetTenantNotificationsResponse[], hasMore: boolean) => void
23
+
24
+ export interface NotificationConfig {
25
+ /**
26
+ * Fetches the notifications according to the options passed as parameters.
27
+ * @param options page, size and filters.
28
+ * @returns a promise that resolves to a {@link ResponseModelGetTenantNotificationsResponse}.
29
+ */
30
+ load(options: LoadNotificationsOptions): Promise<ResponseModelGetTenantNotificationsResponse>,
31
+ /**
32
+ * Check for unread notifications since the date passed as parameter.
33
+ * @param date the date to start the search from
34
+ * @returns a promise that resolves to true if there are unread notifications or false otherwise.
35
+ */
36
+ checkForUnreadNotificationsSince(date?: Date): Promise<boolean>,
37
+ /**
38
+ * Marks the notification with the id passed as parameter as read (committed).
39
+ * @param id the id of the notification.
40
+ */
41
+ markAsCommitted(id: string): Promise<void>,
42
+ /**
43
+ * How long (ms) we should wait before checking for unread notifications again.
44
+ * @default 120000
45
+ */
46
+ pollingMS?: number,
47
+ /**
48
+ * Path to the notifications page.
49
+ * @default '/notifications'
50
+ */
51
+ notificationsPath?: string,
52
+ /**
53
+ * A function to call whenever an action button of a notification is clicked. It receives the id of the notification as a parameter.
54
+ *
55
+ * Useful for adding a behavior other than committing the notification and redirecting to its action. Example: analytics.
56
+ *
57
+ * @param id the id of the notification.
58
+ */
59
+ onClickAction?: (id: string) => void,
60
+ /**
61
+ * Function that is called whenever the "read more" button is clicked.
62
+ */
63
+ onClickViewNotification?: () => void,
64
+ }
65
+
66
+ export type UnreadNotificationListener = (hasUnreadNotification: boolean) => void
package/src/hooks/date.ts CHANGED
@@ -1,31 +1,31 @@
1
- import { Language, useLanguage } from '@stack-spot/portal-translate'
2
- import { format } from 'date-fns'
3
- import { ptBR } from 'date-fns/locale'
4
-
5
- function formatDate(date: string, locale: Language, includeTime: boolean) {
6
- const time = includeTime ? ' HH:mm' : ''
7
- return locale === 'pt'
8
- ? format(new Date(date), `dd/MM/yyyy${time}`, { locale: ptBR })
9
- : format(new Date(date), `MMMM dd yyyy${time}`)
10
- }
11
-
12
- function formatSimpleDate(date: string, locale: Language) {
13
-
14
- return locale === 'pt'
15
- ? format(new Date(date), 'dd/MM/yyyy', { locale: ptBR })
16
- : format(new Date(date), 'MM/dd/yyyy')
17
- }
18
-
19
- /**
20
- * A utility for formatting dates.
21
- * @param date
22
- * @returns a date formatter object.
23
- */
24
- export function useDateFormatter() {
25
- const locale = useLanguage()
26
- return {
27
- formatDate: (date?: string) => date ? formatDate(date, locale, false) : undefined,
28
- formatDateWithTime: (date?: string) => date ? formatDate(date, locale, true) : undefined,
29
- formatSimpleDate: (date?: string) => date ? formatSimpleDate(date, locale) : undefined,
30
- }
31
- }
1
+ import { Language, useLanguage } from '@stack-spot/portal-translate'
2
+ import { format } from 'date-fns'
3
+ import { ptBR } from 'date-fns/locale'
4
+
5
+ function formatDate(date: string, locale: Language, includeTime: boolean) {
6
+ const time = includeTime ? ' HH:mm' : ''
7
+ return locale === 'pt'
8
+ ? format(new Date(date), `dd/MM/yyyy${time}`, { locale: ptBR })
9
+ : format(new Date(date), `MMMM dd yyyy${time}`)
10
+ }
11
+
12
+ function formatSimpleDate(date: string, locale: Language) {
13
+
14
+ return locale === 'pt'
15
+ ? format(new Date(date), 'dd/MM/yyyy', { locale: ptBR })
16
+ : format(new Date(date), 'MM/dd/yyyy')
17
+ }
18
+
19
+ /**
20
+ * A utility for formatting dates.
21
+ * @param date
22
+ * @returns a date formatter object.
23
+ */
24
+ export function useDateFormatter() {
25
+ const locale = useLanguage()
26
+ return {
27
+ formatDate: (date?: string) => date ? formatDate(date, locale, false) : undefined,
28
+ formatDateWithTime: (date?: string) => date ? formatDate(date, locale, true) : undefined,
29
+ formatSimpleDate: (date?: string) => date ? formatSimpleDate(date, locale) : undefined,
30
+ }
31
+ }