@stack-spot/portal-components 0.0.18 → 1.0.0-dev.1770393008395
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 +670 -0
- package/dist/components/AnimatedHeight.d.ts +58 -0
- package/dist/components/AnimatedHeight.d.ts.map +1 -0
- package/dist/components/AnimatedHeight.js +105 -0
- package/dist/components/AnimatedHeight.js.map +1 -0
- package/dist/components/AsyncContent.d.ts +46 -0
- package/dist/components/AsyncContent.d.ts.map +1 -0
- package/dist/components/AsyncContent.js +34 -0
- package/dist/components/AsyncContent.js.map +1 -0
- package/dist/components/BannerWarning.d.ts +11 -2
- package/dist/components/BannerWarning.d.ts.map +1 -1
- package/dist/components/BannerWarning.js +45 -3
- package/dist/components/BannerWarning.js.map +1 -1
- package/dist/components/Breadcrumb/index.d.ts +7 -9
- package/dist/components/Breadcrumb/index.d.ts.map +1 -1
- package/dist/components/Breadcrumb/index.js +7 -6
- package/dist/components/Breadcrumb/index.js.map +1 -1
- package/dist/components/Breadcrumb/styled.d.ts +1 -2
- package/dist/components/Breadcrumb/styled.d.ts.map +1 -1
- package/dist/components/Breadcrumb/styled.js +1 -0
- package/dist/components/Breadcrumb/styled.js.map +1 -1
- package/dist/components/ButtonLoading.d.ts +11 -0
- package/dist/components/ButtonLoading.d.ts.map +1 -0
- package/dist/components/ButtonLoading.js +5 -0
- package/dist/components/ButtonLoading.js.map +1 -0
- package/dist/components/ChatBot.d.ts +5 -0
- package/dist/components/ChatBot.d.ts.map +1 -1
- package/dist/components/ChatBot.js +11 -6
- package/dist/components/ChatBot.js.map +1 -1
- package/dist/components/ContentValidateFilter.d.ts +6 -0
- package/dist/components/ContentValidateFilter.d.ts.map +1 -0
- package/dist/components/ContentValidateFilter.js +8 -0
- package/dist/components/ContentValidateFilter.js.map +1 -0
- package/dist/components/FadingOverflow.d.ts +37 -0
- package/dist/components/FadingOverflow.d.ts.map +1 -0
- package/dist/components/FadingOverflow.js +228 -0
- package/dist/components/FadingOverflow.js.map +1 -0
- package/dist/components/FileTreeView/More.d.ts +23 -0
- package/dist/components/FileTreeView/More.d.ts.map +1 -0
- package/dist/components/FileTreeView/More.js +55 -0
- package/dist/components/FileTreeView/More.js.map +1 -0
- package/dist/components/FileTreeView/index.d.ts +32 -0
- package/dist/components/FileTreeView/index.d.ts.map +1 -0
- package/dist/components/FileTreeView/index.js +91 -0
- package/dist/components/FileTreeView/index.js.map +1 -0
- package/dist/components/InfiniteScroll.d.ts +11 -0
- package/dist/components/InfiniteScroll.d.ts.map +1 -0
- package/dist/components/InfiniteScroll.js +14 -0
- package/dist/components/InfiniteScroll.js.map +1 -0
- package/dist/components/InfoMaintenanceBanner.d.ts +8 -0
- package/dist/components/InfoMaintenanceBanner.d.ts.map +1 -0
- package/dist/components/InfoMaintenanceBanner.js +19 -0
- package/dist/components/InfoMaintenanceBanner.js.map +1 -0
- package/dist/components/LazyMarkdown/BlockquoteMd.d.ts +11 -0
- package/dist/components/LazyMarkdown/BlockquoteMd.d.ts.map +1 -0
- package/dist/components/LazyMarkdown/BlockquoteMd.js +77 -0
- package/dist/components/LazyMarkdown/BlockquoteMd.js.map +1 -0
- package/dist/components/LazyMarkdown/CodeViewer.d.ts +24 -0
- package/dist/components/LazyMarkdown/CodeViewer.d.ts.map +1 -0
- package/dist/components/LazyMarkdown/CodeViewer.js +122 -0
- package/dist/components/LazyMarkdown/CodeViewer.js.map +1 -0
- package/dist/components/LazyMarkdown/Markdown.d.ts +19 -0
- package/dist/components/LazyMarkdown/Markdown.d.ts.map +1 -0
- package/dist/components/LazyMarkdown/Markdown.js +96 -0
- package/dist/components/LazyMarkdown/Markdown.js.map +1 -0
- package/dist/components/LazyMarkdown/MarkdownButton.d.ts +19 -0
- package/dist/components/LazyMarkdown/MarkdownButton.d.ts.map +1 -0
- package/dist/components/LazyMarkdown/MarkdownButton.js +9 -0
- package/dist/components/LazyMarkdown/MarkdownButton.js.map +1 -0
- package/dist/components/LazyMarkdown/Video.d.ts +11 -0
- package/dist/components/LazyMarkdown/Video.d.ts.map +1 -0
- package/dist/components/LazyMarkdown/Video.js +10 -0
- package/dist/components/LazyMarkdown/Video.js.map +1 -0
- package/dist/components/LazyMarkdown/index.d.ts +10 -0
- package/dist/components/LazyMarkdown/index.d.ts.map +1 -0
- package/dist/components/LazyMarkdown/index.js +8 -0
- package/dist/components/LazyMarkdown/index.js.map +1 -0
- package/dist/components/Placeholder.d.ts +28 -0
- package/dist/components/Placeholder.d.ts.map +1 -0
- package/dist/components/Placeholder.js +32 -0
- package/dist/components/Placeholder.js.map +1 -0
- package/dist/components/ScrollView.d.ts +31 -0
- package/dist/components/ScrollView.d.ts.map +1 -0
- package/dist/components/ScrollView.js +30 -0
- package/dist/components/ScrollView.js.map +1 -0
- package/dist/components/Select/BadgeItem.d.ts +4 -0
- package/dist/components/Select/BadgeItem.d.ts.map +1 -0
- package/dist/components/Select/BadgeItem.js +28 -0
- package/dist/components/Select/BadgeItem.js.map +1 -0
- package/dist/components/Select/ClearInput.d.ts +3 -0
- package/dist/components/Select/ClearInput.d.ts.map +1 -0
- package/dist/components/Select/ClearInput.js +18 -0
- package/dist/components/Select/ClearInput.js.map +1 -0
- package/dist/components/Select/CloseItem.d.ts +3 -0
- package/dist/components/Select/CloseItem.d.ts.map +1 -0
- package/dist/components/Select/CloseItem.js +26 -0
- package/dist/components/Select/CloseItem.js.map +1 -0
- package/dist/components/Select/CreatableSelect.d.ts +16 -0
- package/dist/components/Select/CreatableSelect.d.ts.map +1 -0
- package/dist/components/Select/CreatableSelect.js +114 -0
- package/dist/components/Select/CreatableSelect.js.map +1 -0
- package/dist/components/Select/CustomMenu.d.ts +8 -0
- package/dist/components/Select/CustomMenu.d.ts.map +1 -0
- package/dist/components/Select/CustomMenu.js +4 -0
- package/dist/components/Select/CustomMenu.js.map +1 -0
- package/dist/components/Select/LabelItem.d.ts +3 -0
- package/dist/components/Select/LabelItem.d.ts.map +1 -0
- package/dist/components/Select/LabelItem.js +4 -0
- package/dist/components/Select/LabelItem.js.map +1 -0
- package/dist/components/Select/MultiValue.d.ts +3 -0
- package/dist/components/Select/MultiValue.d.ts.map +1 -0
- package/dist/components/Select/MultiValue.js +27 -0
- package/dist/components/Select/MultiValue.js.map +1 -0
- package/dist/components/Select/SelectInfiniteScroll.d.ts +14 -0
- package/dist/components/Select/SelectInfiniteScroll.d.ts.map +1 -0
- package/dist/components/Select/SelectInfiniteScroll.js +31 -0
- package/dist/components/Select/SelectInfiniteScroll.js.map +1 -0
- package/dist/components/Select/SelectSearch.d.ts +11 -0
- package/dist/components/Select/SelectSearch.d.ts.map +1 -0
- package/dist/components/Select/SelectSearch.js +156 -0
- package/dist/components/Select/SelectSearch.js.map +1 -0
- package/dist/components/Select/index.d.ts +6 -0
- package/dist/components/Select/index.d.ts.map +1 -0
- package/dist/components/Select/index.js +6 -0
- package/dist/components/Select/index.js.map +1 -0
- package/dist/components/Select/types.d.ts +7 -0
- package/dist/components/Select/types.d.ts.map +1 -0
- package/dist/components/Select/types.js +2 -0
- package/dist/components/Select/types.js.map +1 -0
- package/dist/components/SelectionList.d.ts +151 -0
- package/dist/components/SelectionList.d.ts.map +1 -0
- package/dist/components/SelectionList.js +180 -0
- package/dist/components/SelectionList.js.map +1 -0
- package/dist/components/StatusCircle.d.ts +24 -0
- package/dist/components/StatusCircle.d.ts.map +1 -0
- package/dist/components/StatusCircle.js +44 -0
- package/dist/components/StatusCircle.js.map +1 -0
- package/dist/components/Stepper/Navigation.d.ts +35 -0
- package/dist/components/Stepper/Navigation.d.ts.map +1 -0
- package/dist/components/Stepper/Navigation.js +41 -0
- package/dist/components/Stepper/Navigation.js.map +1 -0
- package/dist/components/Stepper/Step.d.ts +18 -0
- package/dist/components/Stepper/Step.d.ts.map +1 -0
- package/dist/components/Stepper/Step.js +13 -0
- package/dist/components/Stepper/Step.js.map +1 -0
- package/dist/components/Stepper/Stepper.d.ts +45 -0
- package/dist/components/Stepper/Stepper.d.ts.map +1 -0
- package/dist/components/Stepper/Stepper.js +57 -0
- package/dist/components/Stepper/Stepper.js.map +1 -0
- package/dist/components/Stepper/headers.d.ts +19 -0
- package/dist/components/Stepper/headers.d.ts.map +1 -0
- package/dist/components/Stepper/headers.js +40 -0
- package/dist/components/Stepper/headers.js.map +1 -0
- package/dist/components/Stepper/index.d.ts +4 -0
- package/dist/components/Stepper/index.d.ts.map +1 -0
- package/dist/components/Stepper/index.js +4 -0
- package/dist/components/Stepper/index.js.map +1 -0
- package/dist/components/Table/HeaderItem.d.ts +18 -0
- package/dist/components/Table/HeaderItem.d.ts.map +1 -0
- package/dist/components/Table/HeaderItem.js +14 -0
- package/dist/components/Table/HeaderItem.js.map +1 -0
- package/dist/components/Table/SettingsVerticalMenu.d.ts +14 -0
- package/dist/components/Table/SettingsVerticalMenu.d.ts.map +1 -0
- package/dist/components/Table/SettingsVerticalMenu.js +14 -0
- package/dist/components/Table/SettingsVerticalMenu.js.map +1 -0
- package/dist/components/Table/StyledLinkTable.d.ts +5 -0
- package/dist/components/Table/StyledLinkTable.d.ts.map +1 -0
- package/dist/components/Table/StyledLinkTable.js +13 -0
- package/dist/components/Table/StyledLinkTable.js.map +1 -0
- package/dist/components/Table/TableData.d.ts +37 -0
- package/dist/components/Table/TableData.d.ts.map +1 -0
- package/dist/components/Table/TableData.js +84 -0
- package/dist/components/Table/TableData.js.map +1 -0
- package/dist/components/Table/index.d.ts +3 -0
- package/dist/components/Table/index.d.ts.map +1 -0
- package/dist/components/Table/index.js +3 -0
- package/dist/components/Table/index.js.map +1 -0
- package/dist/components/TimelineSection.d.ts +24 -0
- package/dist/components/TimelineSection.d.ts.map +1 -0
- package/dist/components/TimelineSection.js +37 -0
- package/dist/components/TimelineSection.js.map +1 -0
- package/dist/components/error/ErrorFeedback.d.ts +58 -0
- package/dist/components/error/ErrorFeedback.d.ts.map +1 -0
- package/dist/components/error/ErrorFeedback.js +110 -0
- package/dist/components/error/ErrorFeedback.js.map +1 -0
- package/dist/components/error/NotFound.d.ts +4 -0
- package/dist/components/error/NotFound.d.ts.map +1 -0
- package/dist/components/error/NotFound.js +19 -0
- package/dist/components/error/NotFound.js.map +1 -0
- package/dist/components/error/UnderMaintenance.d.ts +4 -0
- package/dist/components/error/UnderMaintenance.d.ts.map +1 -0
- package/dist/components/error/UnderMaintenance.js +25 -0
- package/dist/components/error/UnderMaintenance.js.map +1 -0
- package/dist/components/error/index.d.ts +4 -0
- package/dist/components/error/index.d.ts.map +1 -0
- package/dist/components/error/index.js +4 -0
- package/dist/components/error/index.js.map +1 -0
- package/dist/components/form/Form/Form.d.ts +15 -0
- package/dist/components/form/Form/Form.d.ts.map +1 -0
- package/dist/components/form/Form/Form.js +48 -0
- package/dist/components/form/Form/Form.js.map +1 -0
- package/dist/components/form/Form/FormGroup.d.ts +37 -0
- package/dist/components/form/Form/FormGroup.d.ts.map +1 -0
- package/dist/components/form/Form/FormGroup.js +72 -0
- package/dist/components/form/Form/FormGroup.js.map +1 -0
- package/dist/components/form/Form/index.d.ts +3 -0
- package/dist/components/form/Form/index.d.ts.map +1 -0
- package/dist/components/form/Form/index.js +3 -0
- package/dist/components/form/Form/index.js.map +1 -0
- package/dist/components/form/SearchInput.d.ts +9 -0
- package/dist/components/form/SearchInput.d.ts.map +1 -0
- package/dist/components/form/SearchInput.js +27 -0
- package/dist/components/form/SearchInput.js.map +1 -0
- package/dist/components/form/Select/CustomSelect.d.ts +81 -0
- package/dist/components/form/Select/CustomSelect.d.ts.map +1 -0
- package/dist/components/form/Select/CustomSelect.js +178 -0
- package/dist/components/form/Select/CustomSelect.js.map +1 -0
- package/dist/components/form/Select/DetailedSelect.d.ts +65 -0
- package/dist/components/form/Select/DetailedSelect.d.ts.map +1 -0
- package/dist/components/form/Select/DetailedSelect.js +80 -0
- package/dist/components/form/Select/DetailedSelect.js.map +1 -0
- package/dist/components/form/Select/Select.d.ts +47 -0
- package/dist/components/form/Select/Select.d.ts.map +1 -0
- package/dist/components/form/Select/Select.js +62 -0
- package/dist/components/form/Select/Select.js.map +1 -0
- package/dist/components/form/Select/index.d.ts +5 -0
- package/dist/components/form/Select/index.d.ts.map +1 -0
- package/dist/components/form/Select/index.js +5 -0
- package/dist/components/form/Select/index.js.map +1 -0
- package/dist/components/form/Select/styled.d.ts +5 -0
- package/dist/components/form/Select/styled.d.ts.map +1 -0
- package/dist/components/form/Select/styled.js +165 -0
- package/dist/components/form/Select/styled.js.map +1 -0
- package/dist/components/form/Select/types.d.ts +103 -0
- package/dist/components/form/Select/types.d.ts.map +1 -0
- package/dist/components/form/Select/types.js +2 -0
- package/dist/components/form/Select/types.js.map +1 -0
- package/dist/components/form/Select/utils.d.ts +3 -0
- package/dist/components/form/Select/utils.d.ts.map +1 -0
- package/dist/components/form/Select/utils.js +20 -0
- package/dist/components/form/Select/utils.js.map +1 -0
- package/dist/components/notification/NotificationComponent.d.ts +31 -0
- package/dist/components/notification/NotificationComponent.d.ts.map +1 -0
- package/dist/components/notification/NotificationComponent.js +176 -0
- package/dist/components/notification/NotificationComponent.js.map +1 -0
- package/dist/components/notification/NotificationItem.d.ts +51 -0
- package/dist/components/notification/NotificationItem.d.ts.map +1 -0
- package/dist/components/notification/NotificationItem.js +165 -0
- package/dist/components/notification/NotificationItem.js.map +1 -0
- package/dist/components/notification/NotificationList.d.ts +47 -0
- package/dist/components/notification/NotificationList.d.ts.map +1 -0
- package/dist/components/notification/NotificationList.js +102 -0
- package/dist/components/notification/NotificationList.js.map +1 -0
- package/dist/components/notification/types.d.ts +60 -0
- package/dist/components/notification/types.d.ts.map +1 -0
- package/dist/components/notification/types.js +7 -0
- package/dist/components/notification/types.js.map +1 -0
- package/dist/containers/NotificationsPage.d.ts +2 -0
- package/dist/containers/NotificationsPage.d.ts.map +1 -0
- package/dist/containers/NotificationsPage.js +72 -0
- package/dist/containers/NotificationsPage.js.map +1 -0
- package/dist/context/anchor.d.ts +27 -0
- package/dist/context/anchor.d.ts.map +1 -0
- package/dist/context/anchor.js +23 -0
- package/dist/context/anchor.js.map +1 -0
- package/dist/context/loading.d.ts +27 -0
- package/dist/context/loading.d.ts.map +1 -0
- package/dist/context/loading.js +24 -0
- package/dist/context/loading.js.map +1 -0
- package/dist/context/notification/LazyNotificationList.d.ts +28 -0
- package/dist/context/notification/LazyNotificationList.d.ts.map +1 -0
- package/dist/context/notification/LazyNotificationList.js +136 -0
- package/dist/context/notification/LazyNotificationList.js.map +1 -0
- package/dist/context/notification/NotificationController.d.ts +25 -0
- package/dist/context/notification/NotificationController.d.ts.map +1 -0
- package/dist/context/notification/NotificationController.js +153 -0
- package/dist/context/notification/NotificationController.js.map +1 -0
- package/dist/context/notification/context.d.ts +8 -0
- package/dist/context/notification/context.d.ts.map +1 -0
- package/dist/context/notification/context.js +12 -0
- package/dist/context/notification/context.js.map +1 -0
- package/dist/context/notification/hooks.d.ts +18 -0
- package/dist/context/notification/hooks.d.ts.map +1 -0
- package/dist/context/notification/hooks.js +104 -0
- package/dist/context/notification/hooks.js.map +1 -0
- package/dist/context/notification/types.d.ts +65 -0
- package/dist/context/notification/types.d.ts.map +1 -0
- package/dist/context/notification/types.js +2 -0
- package/dist/context/notification/types.js.map +1 -0
- package/dist/hooks/date.d.ts +18 -0
- package/dist/hooks/date.d.ts.map +1 -0
- package/dist/hooks/date.js +68 -0
- package/dist/hooks/date.js.map +1 -0
- package/dist/hooks/keyboard.d.ts +64 -0
- package/dist/hooks/keyboard.d.ts.map +1 -0
- package/dist/hooks/keyboard.js +84 -0
- package/dist/hooks/keyboard.js.map +1 -0
- package/dist/hooks/manual-render.d.ts +8 -0
- package/dist/hooks/manual-render.d.ts.map +1 -0
- package/dist/hooks/manual-render.js +10 -0
- package/dist/hooks/manual-render.js.map +1 -0
- package/dist/hooks/service-now.d.ts +32 -1
- package/dist/hooks/service-now.d.ts.map +1 -1
- package/dist/hooks/service-now.js +58 -18
- package/dist/hooks/service-now.js.map +1 -1
- package/dist/hooks/text.d.ts +9 -0
- package/dist/hooks/text.d.ts.map +1 -0
- package/dist/hooks/text.js +24 -0
- package/dist/hooks/text.js.map +1 -0
- package/dist/hooks/title.d.ts +13 -0
- package/dist/hooks/title.d.ts.map +1 -1
- package/dist/hooks/title.js +13 -0
- package/dist/hooks/title.js.map +1 -1
- package/dist/hooks/use-effect-once.d.ts.map +1 -1
- package/dist/hooks/use-effect-once.js.map +1 -1
- package/dist/index.d.ts +14 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +14 -4
- package/dist/index.js.map +1 -1
- package/dist/notifications.d.ts +11 -0
- package/dist/notifications.d.ts.map +1 -0
- package/dist/notifications.js +10 -0
- package/dist/notifications.js.map +1 -0
- package/dist/svg/AI.d.ts +8 -0
- package/dist/svg/AI.d.ts.map +1 -0
- package/dist/svg/AI.js +15 -0
- package/dist/svg/AI.js.map +1 -0
- package/dist/svg/CS.d.ts +8 -0
- package/dist/svg/CS.d.ts.map +1 -0
- package/dist/svg/CS.js +15 -0
- package/dist/svg/CS.js.map +1 -0
- package/dist/svg/EDP.d.ts +8 -0
- package/dist/svg/EDP.d.ts.map +1 -0
- package/dist/svg/EDP.js +15 -0
- package/dist/svg/EDP.js.map +1 -0
- package/dist/svg/Forbidden.d.ts +5 -0
- package/dist/svg/Forbidden.d.ts.map +1 -0
- package/dist/svg/Forbidden.js +4 -0
- package/dist/svg/Forbidden.js.map +1 -0
- package/dist/svg/GenericPlaceholder.d.ts +7 -0
- package/dist/svg/GenericPlaceholder.d.ts.map +1 -0
- package/dist/svg/GenericPlaceholder.js +4 -0
- package/dist/svg/GenericPlaceholder.js.map +1 -0
- package/dist/svg/GenericPlaceholderCentered.d.ts +7 -0
- package/dist/svg/GenericPlaceholderCentered.d.ts.map +1 -0
- package/dist/svg/GenericPlaceholderCentered.js +4 -0
- package/dist/svg/GenericPlaceholderCentered.js.map +1 -0
- package/dist/svg/HUB.d.ts +8 -0
- package/dist/svg/HUB.d.ts.map +1 -0
- package/dist/svg/HUB.js +15 -0
- package/dist/svg/HUB.js.map +1 -0
- package/dist/svg/Logo.d.ts +5 -0
- package/dist/svg/Logo.d.ts.map +1 -0
- package/dist/svg/Logo.js +8 -0
- package/dist/svg/Logo.js.map +1 -0
- package/dist/svg/MiniLogo.d.ts +8 -0
- package/dist/svg/MiniLogo.d.ts.map +1 -0
- package/dist/svg/MiniLogo.js +6 -0
- package/dist/svg/MiniLogo.js.map +1 -0
- package/dist/svg/NotFound.d.ts +5 -0
- package/dist/svg/NotFound.d.ts.map +1 -0
- package/dist/svg/NotFound.js +4 -0
- package/dist/svg/NotFound.js.map +1 -0
- package/dist/svg/ServerError.d.ts +5 -0
- package/dist/svg/ServerError.d.ts.map +1 -0
- package/dist/svg/ServerError.js +4 -0
- package/dist/svg/ServerError.js.map +1 -0
- package/dist/svg/Unauthenticated.d.ts +5 -0
- package/dist/svg/Unauthenticated.d.ts.map +1 -0
- package/dist/svg/Unauthenticated.js +4 -0
- package/dist/svg/Unauthenticated.js.map +1 -0
- package/dist/svg/index.d.ts +13 -0
- package/dist/svg/index.d.ts.map +1 -0
- package/dist/svg/index.js +14 -0
- package/dist/svg/index.js.map +1 -0
- package/dist/utils/accessibility.d.ts +75 -0
- package/dist/utils/accessibility.d.ts.map +1 -0
- package/dist/utils/accessibility.js +123 -0
- package/dist/utils/accessibility.js.map +1 -0
- package/dist/utils/cookie.d.ts +34 -0
- package/dist/utils/cookie.d.ts.map +1 -0
- package/dist/utils/cookie.js +69 -0
- package/dist/utils/cookie.js.map +1 -0
- package/dist/utils/promise.d.ts +2 -0
- package/dist/utils/promise.d.ts.map +1 -0
- package/dist/utils/promise.js +6 -0
- package/dist/utils/promise.js.map +1 -0
- package/dist/utils/read-file.d.ts +2 -0
- package/dist/utils/read-file.d.ts.map +1 -0
- package/dist/utils/read-file.js +14 -0
- package/dist/utils/read-file.js.map +1 -0
- package/package.dev.json +3 -0
- package/package.json +54 -14
- package/package.stg.json +3 -0
- package/readme.md +55 -13
- package/src/components/AnimatedHeight.tsx +174 -0
- package/src/components/AsyncContent.tsx +78 -0
- package/src/components/BannerWarning.tsx +89 -19
- package/src/components/Breadcrumb/index.tsx +15 -13
- package/src/components/Breadcrumb/styled.ts +2 -1
- package/src/components/ButtonLoading.tsx +29 -0
- package/src/components/ChatBot.tsx +12 -7
- package/src/components/ContentValidateFilter.tsx +15 -0
- package/src/components/FadingOverflow.tsx +265 -0
- package/src/components/FileTreeView/More.tsx +114 -0
- package/src/components/FileTreeView/index.tsx +186 -0
- package/src/components/InfiniteScroll.tsx +24 -0
- package/src/components/InfoMaintenanceBanner.tsx +25 -0
- package/src/components/LazyMarkdown/BlockquoteMd.tsx +107 -0
- package/src/components/LazyMarkdown/CodeViewer.tsx +161 -0
- package/src/components/LazyMarkdown/Markdown.tsx +122 -0
- package/src/components/LazyMarkdown/MarkdownButton.tsx +24 -0
- package/src/components/LazyMarkdown/Video.tsx +13 -0
- package/src/components/LazyMarkdown/index.tsx +21 -0
- package/src/components/Placeholder.tsx +123 -0
- package/src/components/ScrollView.tsx +57 -0
- package/src/components/Select/BadgeItem.tsx +58 -0
- package/src/components/Select/ClearInput.tsx +24 -0
- package/src/components/Select/CloseItem.tsx +38 -0
- package/src/components/Select/CreatableSelect.tsx +155 -0
- package/src/components/Select/CustomMenu.tsx +16 -0
- package/src/components/Select/LabelItem.tsx +8 -0
- package/src/components/Select/MultiValue.tsx +49 -0
- package/src/components/Select/SelectInfiniteScroll.tsx +82 -0
- package/src/components/Select/SelectSearch.tsx +195 -0
- package/src/components/Select/index.tsx +7 -0
- package/src/components/Select/types.ts +8 -0
- package/src/components/SelectionList.tsx +427 -0
- package/src/components/StatusCircle.tsx +67 -0
- package/src/components/Stepper/Navigation.tsx +97 -0
- package/src/components/Stepper/Step.tsx +30 -0
- package/src/components/Stepper/Stepper.tsx +113 -0
- package/src/components/Stepper/headers.tsx +64 -0
- package/src/components/Stepper/index.ts +3 -0
- package/src/components/Table/HeaderItem.tsx +52 -0
- package/src/components/Table/SettingsVerticalMenu.tsx +50 -0
- package/src/components/Table/StyledLinkTable.tsx +22 -0
- package/src/components/Table/TableData.tsx +251 -0
- package/src/components/Table/index.tsx +2 -0
- package/src/components/TimelineSection.tsx +66 -0
- package/src/components/error/ErrorFeedback.tsx +217 -0
- package/src/components/error/NotFound.tsx +24 -0
- package/src/components/error/UnderMaintenance.tsx +30 -0
- package/src/components/error/index.ts +4 -0
- package/src/components/form/Form/Form.tsx +101 -0
- package/src/components/form/Form/FormGroup.tsx +221 -0
- package/src/components/form/Form/index.ts +2 -0
- package/src/components/form/SearchInput.tsx +72 -0
- package/src/components/form/Select/CustomSelect.tsx +232 -0
- package/src/components/form/Select/DetailedSelect.tsx +85 -0
- package/src/components/form/Select/Select.tsx +67 -0
- package/src/components/form/Select/index.ts +4 -0
- package/src/components/form/Select/styled.ts +165 -0
- package/src/components/form/Select/types.ts +112 -0
- package/src/components/form/Select/utils.tsx +28 -0
- package/src/components/notification/NotificationComponent.tsx +340 -0
- package/src/components/notification/NotificationItem.tsx +353 -0
- package/src/components/notification/NotificationList.tsx +207 -0
- package/src/components/notification/types.ts +72 -0
- package/src/containers/NotificationsPage.tsx +121 -0
- package/src/context/anchor.tsx +37 -0
- package/src/context/loading.tsx +36 -0
- package/src/context/notification/LazyNotificationList.ts +103 -0
- package/src/context/notification/NotificationController.ts +119 -0
- package/src/context/notification/context.tsx +23 -0
- package/src/context/notification/hooks.ts +115 -0
- package/src/context/notification/types.ts +71 -0
- package/src/hooks/date.ts +68 -0
- package/src/hooks/keyboard.tsx +128 -0
- package/src/hooks/manual-render.tsx +10 -0
- package/src/hooks/service-now.tsx +64 -19
- package/src/hooks/text.tsx +30 -0
- package/src/hooks/title.tsx +13 -0
- package/src/index.ts +14 -6
- package/src/notifications.ts +11 -0
- package/src/svg/AI.tsx +41 -0
- package/src/svg/CS.tsx +48 -0
- package/src/svg/EDP.tsx +31 -0
- package/src/svg/Forbidden.tsx +22 -0
- package/src/svg/GenericPlaceholder.tsx +16 -0
- package/src/svg/GenericPlaceholderCentered.tsx +16 -0
- package/src/svg/HUB.tsx +48 -0
- package/src/svg/Logo.tsx +16 -0
- package/src/svg/MiniLogo.tsx +12 -0
- package/src/svg/NotFound.tsx +16 -0
- package/src/svg/ServerError.tsx +33 -0
- package/src/svg/Unauthenticated.tsx +16 -0
- package/src/svg/index.ts +14 -0
- package/src/utils/accessibility.ts +135 -0
- package/src/utils/cookie.ts +73 -0
- package/src/utils/promise.ts +5 -0
- package/src/utils/read-file.ts +16 -0
- package/tsconfig.json +1 -0
- package/dist/components/Login.d.ts +0 -26
- package/dist/components/Login.d.ts.map +0 -1
- package/dist/components/Login.js +0 -100
- package/dist/components/Login.js.map +0 -1
- package/dist/components/MiniLogo.d.ts +0 -6
- package/dist/components/MiniLogo.d.ts.map +0 -1
- package/dist/components/MiniLogo.js +0 -4
- package/dist/components/MiniLogo.js.map +0 -1
- package/dist/components/tour/StepContainer.d.ts +0 -13
- package/dist/components/tour/StepContainer.d.ts.map +0 -1
- package/dist/components/tour/StepContainer.js +0 -48
- package/dist/components/tour/StepContainer.js.map +0 -1
- package/dist/components/tour/StepNavigation.d.ts +0 -13
- package/dist/components/tour/StepNavigation.d.ts.map +0 -1
- package/dist/components/tour/StepNavigation.js +0 -20
- package/dist/components/tour/StepNavigation.js.map +0 -1
- package/dist/components/tour/StepTitle.d.ts +0 -7
- package/dist/components/tour/StepTitle.d.ts.map +0 -1
- package/dist/components/tour/StepTitle.js +0 -5
- package/dist/components/tour/StepTitle.js.map +0 -1
- package/dist/components/tour/context.d.ts +0 -17
- package/dist/components/tour/context.d.ts.map +0 -1
- package/dist/components/tour/context.js +0 -48
- package/dist/components/tour/context.js.map +0 -1
- package/dist/components/tour/index.d.ts +0 -9
- package/dist/components/tour/index.d.ts.map +0 -1
- package/dist/components/tour/index.js +0 -4
- package/dist/components/tour/index.js.map +0 -1
- package/dist/components/tour/utils.d.ts +0 -13
- package/dist/components/tour/utils.d.ts.map +0 -1
- package/dist/components/tour/utils.js +0 -43
- package/dist/components/tour/utils.js.map +0 -1
- package/src/components/Login.tsx +0 -157
- package/src/components/MiniLogo.tsx +0 -32
- package/src/components/tour/StepContainer.tsx +0 -67
- package/src/components/tour/StepNavigation.tsx +0 -32
- package/src/components/tour/StepTitle.tsx +0 -17
- package/src/components/tour/context.tsx +0 -56
- package/src/components/tour/index.ts +0 -7
- package/src/components/tour/utils.tsx +0 -65
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { Text } from '@citric/core'
|
|
2
|
+
import { Dictionary, useTranslate } from '@stack-spot/portal-translate'
|
|
3
|
+
import { styled } from 'styled-components'
|
|
4
|
+
import { AsyncContent } from '../components/AsyncContent'
|
|
5
|
+
import { ErrorFeedback } from '../components/error'
|
|
6
|
+
import { SearchInput } from '../components/form/SearchInput'
|
|
7
|
+
import { Select } from '../components/form/Select'
|
|
8
|
+
import { NotificationList } from '../components/notification/NotificationList'
|
|
9
|
+
import { useNotificationList } from '../context/notification/hooks'
|
|
10
|
+
import { NotificationContext, NotificationPriority } from '../context/notification/types'
|
|
11
|
+
import { useNotificationController } from '../notifications'
|
|
12
|
+
|
|
13
|
+
const FilterBox = styled.div`
|
|
14
|
+
margin: 24px 0;
|
|
15
|
+
display: flex;
|
|
16
|
+
flex-direction: row;
|
|
17
|
+
gap: 8px;
|
|
18
|
+
|
|
19
|
+
> * {
|
|
20
|
+
flex: 1;
|
|
21
|
+
}
|
|
22
|
+
`
|
|
23
|
+
|
|
24
|
+
const criticalities: NotificationPriority[] = ['HIGH', 'MEDIUM', 'LOW']
|
|
25
|
+
const contexts: NotificationContext[] = ['ACCOUNT', 'STUDIO', 'WORKSPACE']
|
|
26
|
+
type TimeKey = `time.${1 | 7 | 30 | 90 | 365}`
|
|
27
|
+
const times = [1, 7, 30, 90, 365]
|
|
28
|
+
|
|
29
|
+
export const NotificationsPage = () => {
|
|
30
|
+
const t = useTranslate(dictionary)
|
|
31
|
+
const controller = useNotificationController()
|
|
32
|
+
const { hasMore, items, loadMore, applyFilters, filters, status, error } = useNotificationList()
|
|
33
|
+
const hasFilters = Object.entries(filters).length > 0
|
|
34
|
+
|
|
35
|
+
return (<>
|
|
36
|
+
<header><Text appearance="h2" as="h1">{t.title}</Text></header>
|
|
37
|
+
<Text appearance="body2" colorScheme="light.700">
|
|
38
|
+
{t.description}
|
|
39
|
+
</Text>
|
|
40
|
+
<FilterBox>
|
|
41
|
+
<SearchInput searchText={t.filter} defaultValue={filters.search} onChange={search => applyFilters({ search })} />
|
|
42
|
+
<Select
|
|
43
|
+
value={filters.createdSince}
|
|
44
|
+
options={times}
|
|
45
|
+
onChange={createdSince => applyFilters({ createdSince })}
|
|
46
|
+
emptyOption={t.anyTime}
|
|
47
|
+
renderLabel={o => t[`time.${o}` as TimeKey]}
|
|
48
|
+
/>
|
|
49
|
+
<Select
|
|
50
|
+
value={filters.criticality}
|
|
51
|
+
options={criticalities}
|
|
52
|
+
onChange={criticality => applyFilters({ criticality })}
|
|
53
|
+
emptyOption={t.allCriticalities}
|
|
54
|
+
renderLabel={o => t[`criticality.${o}`]}
|
|
55
|
+
/>
|
|
56
|
+
<Select
|
|
57
|
+
value={filters.context}
|
|
58
|
+
options={contexts}
|
|
59
|
+
onChange={context => applyFilters({ context })}
|
|
60
|
+
emptyOption={t.allContexts}
|
|
61
|
+
renderLabel={o => t[`context.${o}`]}
|
|
62
|
+
/>
|
|
63
|
+
</FilterBox>
|
|
64
|
+
<AsyncContent
|
|
65
|
+
error={error}
|
|
66
|
+
loading={status === 'startup'}
|
|
67
|
+
errorDetails={{ errorComponent: () => <ErrorFeedback code={error.code} />, reportError: () => { } }}
|
|
68
|
+
>
|
|
69
|
+
<NotificationList
|
|
70
|
+
items={items}
|
|
71
|
+
loading={status === 'loading'}
|
|
72
|
+
onCommit={id => controller.markAsCommitted(id)}
|
|
73
|
+
onUncommit={id => controller.markAsUncommitted(id)}
|
|
74
|
+
infiniteScroll={{ hasMore, loadMore }}
|
|
75
|
+
onClickAction={controller.config.onClickAction}
|
|
76
|
+
showEmptySearch={hasFilters}
|
|
77
|
+
/>
|
|
78
|
+
</AsyncContent>
|
|
79
|
+
</>)
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const dictionary = {
|
|
83
|
+
en: {
|
|
84
|
+
title: 'Notifications',
|
|
85
|
+
description: 'Here you can view all your received notifications.',
|
|
86
|
+
filter: 'Filter',
|
|
87
|
+
allCriticalities: 'All severities',
|
|
88
|
+
'criticality.HIGH': 'High',
|
|
89
|
+
'criticality.MEDIUM': 'Medium',
|
|
90
|
+
'criticality.LOW': 'Low',
|
|
91
|
+
allContexts: 'All contexts',
|
|
92
|
+
'context.ACCOUNT': 'Account',
|
|
93
|
+
'context.STUDIO': 'Studio',
|
|
94
|
+
'context.WORKSPACE': 'Workspace',
|
|
95
|
+
'anyTime': 'Any time',
|
|
96
|
+
'time.1': 'Last 24 hours',
|
|
97
|
+
'time.7': 'Last 7 days',
|
|
98
|
+
'time.30': 'Last 30 days',
|
|
99
|
+
'time.90': 'Last 90 days',
|
|
100
|
+
'time.365': 'Last 12 months',
|
|
101
|
+
},
|
|
102
|
+
pt: {
|
|
103
|
+
title: 'Notificações',
|
|
104
|
+
description: 'Aqui você encontra todas as notificações recebidas.',
|
|
105
|
+
filter: 'Filtrar',
|
|
106
|
+
allCriticalities: 'Todas as criticidades',
|
|
107
|
+
'criticality.HIGH': 'Alto',
|
|
108
|
+
'criticality.MEDIUM': 'Médio',
|
|
109
|
+
'criticality.LOW': 'Baixo',
|
|
110
|
+
allContexts: 'Todos os contextos',
|
|
111
|
+
'context.ACCOUNT': 'Conta',
|
|
112
|
+
'context.STUDIO': 'Estúdio',
|
|
113
|
+
'context.WORKSPACE': 'Workspace',
|
|
114
|
+
'anyTime': 'Todos os períodos',
|
|
115
|
+
'time.1': 'Últimas 24 horas',
|
|
116
|
+
'time.7': 'Últimos 7 dias',
|
|
117
|
+
'time.30': 'Últimos 30 dias',
|
|
118
|
+
'time.90': 'Últimos 90 dias',
|
|
119
|
+
'time.365': 'Últimos 12 meses',
|
|
120
|
+
},
|
|
121
|
+
} satisfies Dictionary
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { createContext, useContext } from 'react'
|
|
2
|
+
|
|
3
|
+
export type AnchorComponent = (props: React.AnchorHTMLAttributes<HTMLAnchorElement>) => React.ReactElement
|
|
4
|
+
|
|
5
|
+
interface AnchorContext {
|
|
6
|
+
/**
|
|
7
|
+
* The component to render by a layout component a link is needed.
|
|
8
|
+
* @default <a>
|
|
9
|
+
*/
|
|
10
|
+
anchorTag?: AnchorComponent,
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const context = createContext<AnchorContext>({})
|
|
14
|
+
|
|
15
|
+
const Anchor: AnchorComponent = props => <a {...props} />
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Some components need to create HTML anchors (links) to other pages in the website. These links, sometimes, must to be managed by a
|
|
19
|
+
* navigator for React. Since we don't want to couple this library with any specific navigator, you can provide your own component for
|
|
20
|
+
* creating links, it must follow the same interface of the HTML's `a` tag.
|
|
21
|
+
*
|
|
22
|
+
* If this is not used, the tag `<a>` is used by default.
|
|
23
|
+
* @param props the anchor component (anchorTag) and the content to render (children).
|
|
24
|
+
*/
|
|
25
|
+
export const AnchorProvider = ({ children, ...props }: Required<AnchorContext> & { children: React.ReactNode }) => (
|
|
26
|
+
<context.Provider value={props}>{children}</context.Provider>
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* A React hook for retrieving the Link (anchor) component.
|
|
31
|
+
* @returns the link component declared at {@link AnchorProvider} or a component that renders the tag <a> from HTML if no link component was
|
|
32
|
+
* provided.
|
|
33
|
+
*/
|
|
34
|
+
export function useAnchorTag(): AnchorComponent {
|
|
35
|
+
const { anchorTag } = useContext(context)
|
|
36
|
+
return anchorTag ?? Anchor
|
|
37
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { LoadingCircular } from '@citric/ui'
|
|
2
|
+
import { createContext, ReactElement, useContext } from 'react'
|
|
3
|
+
|
|
4
|
+
interface LoadingContext {
|
|
5
|
+
/**
|
|
6
|
+
* The component to render by a layout component.
|
|
7
|
+
* @default <LoadingCircular>
|
|
8
|
+
*/
|
|
9
|
+
loading?: ReactElement,
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const context = createContext<LoadingContext>({})
|
|
13
|
+
|
|
14
|
+
const Loading: ReactElement = <LoadingCircular size="sm" sx={{ position: 'absolute' }} />
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Some portals use a different loading style than what we have in citric.
|
|
18
|
+
* You can provide your own loading component to use wherever you want within the application.
|
|
19
|
+
*
|
|
20
|
+
* @param position The position parameter is mandatory to be passed as absolute in the loading component that is being passed.
|
|
21
|
+
*
|
|
22
|
+
* If this is not used, the component `LoadingCircular` from citric is used by default.
|
|
23
|
+
* @param props the loading component (useLoadingComponent) and the content to render (children).
|
|
24
|
+
*/
|
|
25
|
+
export const LoadingProvider = ({ children, ...props }: Required<LoadingContext> & { children: React.ReactNode }) => (
|
|
26
|
+
<context.Provider value={props}>{children}</context.Provider>
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* A React hook for retrieving the Loading component.
|
|
31
|
+
* @returns the loading component declared at `LoadingProvider`.
|
|
32
|
+
*/
|
|
33
|
+
export function useLoadingComponent(): ReactElement {
|
|
34
|
+
const { loading } = useContext(context)
|
|
35
|
+
return loading ?? Loading
|
|
36
|
+
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { pull, uniqBy } from 'lodash'
|
|
2
|
+
import { GetTenantNotificationsResponse, ResponseModelGetTenantNotificationsResponse } from '../../notifications'
|
|
3
|
+
import { LazyNotificationListener, LoadNotificationsFilters, LoadNotificationsOptions } from './types'
|
|
4
|
+
|
|
5
|
+
interface ConstructorParams {
|
|
6
|
+
id: number,
|
|
7
|
+
load: (options: LoadNotificationsOptions) => Promise<ResponseModelGetTenantNotificationsResponse>,
|
|
8
|
+
filters?: LoadNotificationsFilters,
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export class LazyNotificationList {
|
|
12
|
+
readonly id: number
|
|
13
|
+
items: GetTenantNotificationsResponse[] = []
|
|
14
|
+
private page = -1
|
|
15
|
+
private total = 0
|
|
16
|
+
private filters: LoadNotificationsFilters
|
|
17
|
+
private readonly load: (options: LoadNotificationsOptions) => Promise<ResponseModelGetTenantNotificationsResponse>
|
|
18
|
+
private listeners: LazyNotificationListener[] = []
|
|
19
|
+
private currentFetch: Promise<void> | undefined
|
|
20
|
+
|
|
21
|
+
constructor({ id, load, filters = {} }: ConstructorParams) {
|
|
22
|
+
this.id = id
|
|
23
|
+
this.load = load
|
|
24
|
+
this.filters = filters
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
private notify() {
|
|
28
|
+
const hasMore = this.hasMore()
|
|
29
|
+
this.listeners.forEach(l => l(this.items, hasMore))
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
async #loadMore(reset = false): Promise<void> {
|
|
33
|
+
try {
|
|
34
|
+
const result = await this.load({ ...this.filters, page: this.page + 1 })
|
|
35
|
+
if (reset) {
|
|
36
|
+
this.items = []
|
|
37
|
+
this.total = 0
|
|
38
|
+
}
|
|
39
|
+
// we can't have items with the same id: this can happen if new notifications have been created after the first page was loaded.
|
|
40
|
+
this.items = uniqBy([...this.items, ...result.items], 'id')
|
|
41
|
+
// we can't keep loading more if we already loaded every item or if the API returned an empty list.
|
|
42
|
+
this.total = result.items.length ? (this.total || result.total) : this.items.length
|
|
43
|
+
this.page++
|
|
44
|
+
this.notify()
|
|
45
|
+
} finally {
|
|
46
|
+
this.currentFetch = undefined
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
async applyFilters(filters: LoadNotificationsFilters) {
|
|
51
|
+
try {
|
|
52
|
+
await this.currentFetch
|
|
53
|
+
} catch {
|
|
54
|
+
/* empty: this error will be treated by the function who actually triggered `currentFetch`. We just need to make sure we wait until
|
|
55
|
+
it ends before making new requests. */
|
|
56
|
+
}
|
|
57
|
+
const prevPage = this.page
|
|
58
|
+
const prevFilters = this.filters
|
|
59
|
+
this.filters = filters
|
|
60
|
+
this.page = -1
|
|
61
|
+
try {
|
|
62
|
+
|
|
63
|
+
this.currentFetch = this.#loadMore(true)
|
|
64
|
+
await this.currentFetch
|
|
65
|
+
} catch (error) {
|
|
66
|
+
this.filters = prevFilters
|
|
67
|
+
this.page = prevPage
|
|
68
|
+
throw error
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
loadMore() {
|
|
73
|
+
this.currentFetch ??= this.#loadMore()
|
|
74
|
+
return this.currentFetch
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
hasMore() {
|
|
78
|
+
return this.items.length < this.total
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
subscribe(listener: LazyNotificationListener) {
|
|
82
|
+
this.listeners.push(listener)
|
|
83
|
+
return () => {
|
|
84
|
+
pull(this.listeners, listener)
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
update(readStatusMap: Map<string, boolean>) {
|
|
89
|
+
this.items.forEach(i => i.committed = readStatusMap.get(i.id) ?? i.committed)
|
|
90
|
+
if (this.filters.committed !== undefined) {
|
|
91
|
+
this.items = this.items.filter(i => i.committed === this.filters.committed)
|
|
92
|
+
// if the filtered list has one item or less, we update the list with the backend so it can show more items.
|
|
93
|
+
// we can't use loadMore in this case because the page 2 would now skip some items. We'd need the backend to implement a cursor
|
|
94
|
+
// interface.
|
|
95
|
+
if (this.items.length <= 1) this.applyFilters(this.filters)
|
|
96
|
+
}
|
|
97
|
+
this.notify()
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
mute() {
|
|
101
|
+
this.listeners = []
|
|
102
|
+
}
|
|
103
|
+
}
|
|
@@ -0,0 +1,119 @@
|
|
|
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
|
+
async markAsUncommitted(notificationId: string) {
|
|
84
|
+
if (!this.readStatusMap.get(notificationId)) return
|
|
85
|
+
this.readStatusMap.set(notificationId, false)
|
|
86
|
+
this.lazyLists.forEach(l => l.update(this.readStatusMap))
|
|
87
|
+
try {
|
|
88
|
+
await this.config.markAsUncommitted(notificationId)
|
|
89
|
+
// update the notification indicator: this is an inconsistency, we should actually ask the API, but since it's an expensive
|
|
90
|
+
// operation in the backend, we won't, instead, we'll only check the notifications we have loaded in memory.
|
|
91
|
+
if (this.hasUnreadNotificationInMemory()) this.setUnreadNotification(true)
|
|
92
|
+
} catch {
|
|
93
|
+
this.readStatusMap.set(notificationId, true)
|
|
94
|
+
this.lazyLists.forEach(l => l.update(this.readStatusMap))
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
onUnreadNotificationChange(listener: UnreadNotificationListener) {
|
|
99
|
+
this.unreadNotificationListeners.push(listener)
|
|
100
|
+
return () => {
|
|
101
|
+
pull(this.unreadNotificationListeners, listener)
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
hasUnreadNotification() {
|
|
106
|
+
return this.unreadNotification
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
reset() {
|
|
110
|
+
this.lazyLists.clear()
|
|
111
|
+
this.readStatusMap.clear()
|
|
112
|
+
this.lastUnreadVerification = undefined
|
|
113
|
+
this.unreadNotification = false
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
mute() {
|
|
117
|
+
this.unreadNotificationListeners = []
|
|
118
|
+
}
|
|
119
|
+
}
|
|
@@ -0,0 +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
|
+
}
|
|
@@ -0,0 +1,115 @@
|
|
|
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
|
+
function cleanFilters<T extends Record<string, unknown>>(filters: T): T {
|
|
11
|
+
const cleaned = { ...filters }
|
|
12
|
+
|
|
13
|
+
const keys = Object.keys(cleaned) as (keyof T)[]
|
|
14
|
+
keys.forEach((key) => {
|
|
15
|
+
const value = cleaned[key]
|
|
16
|
+
|
|
17
|
+
if (value === undefined || value === null || value === '') {
|
|
18
|
+
delete cleaned[key]
|
|
19
|
+
}
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
return cleaned
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function useNotificationList(initialFilters: LoadNotificationsFilters = {}) {
|
|
26
|
+
const { repaint } = useManualRender()
|
|
27
|
+
const [status, setStatus] = useState<'startup' | 'idle' | 'error' | 'loading'>('startup')
|
|
28
|
+
const error = useRef<any>()
|
|
29
|
+
const controller = useNotificationController()
|
|
30
|
+
const [filters, setFilters] = useState(initialFilters)
|
|
31
|
+
const list = useRef<LazyNotificationList | undefined>()
|
|
32
|
+
|
|
33
|
+
useEffectOnce(() => {
|
|
34
|
+
async function start() {
|
|
35
|
+
list.current = controller.createLazyNotificationList(initialFilters)
|
|
36
|
+
list.current.subscribe(repaint)
|
|
37
|
+
try {
|
|
38
|
+
await list.current?.loadMore()
|
|
39
|
+
setStatus('idle')
|
|
40
|
+
} catch (e) {
|
|
41
|
+
setStatus('error')
|
|
42
|
+
error.current = e
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
start()
|
|
46
|
+
return () => {
|
|
47
|
+
if (list.current) controller.destroyLazyNotificationList(list.current.id)
|
|
48
|
+
}
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
const applyFilters = useCallback(async (newFilters: LoadNotificationsFilters) => {
|
|
52
|
+
if (!list.current) return
|
|
53
|
+
setStatus('loading')
|
|
54
|
+
// 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
|
|
55
|
+
// value is from the function "setFilters". But "setFilters" run async. For this reason, we wait the function to run and retrieve the
|
|
56
|
+
// current value before continuing, hence the use of the await and promise below.
|
|
57
|
+
const { next, prev } = await new Promise<{ next: LoadNotificationsFilters, prev: LoadNotificationsFilters }>((resolve) => {
|
|
58
|
+
setFilters((filters) => {
|
|
59
|
+
const prev = filters
|
|
60
|
+
const merged = { ...filters, ...newFilters }
|
|
61
|
+
const next = cleanFilters(merged)
|
|
62
|
+
|
|
63
|
+
resolve({ prev, next })
|
|
64
|
+
return next
|
|
65
|
+
})
|
|
66
|
+
})
|
|
67
|
+
try {
|
|
68
|
+
await list.current.applyFilters(next)
|
|
69
|
+
setStatus('idle')
|
|
70
|
+
error.current = undefined
|
|
71
|
+
} catch (e) {
|
|
72
|
+
setFilters(prev)
|
|
73
|
+
if (error.current) {
|
|
74
|
+
error.current = e
|
|
75
|
+
setStatus('error')
|
|
76
|
+
} else {
|
|
77
|
+
setStatus('idle')
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}, [])
|
|
81
|
+
|
|
82
|
+
return {
|
|
83
|
+
status,
|
|
84
|
+
items: list.current?.items ?? [],
|
|
85
|
+
hasMore: list.current?.hasMore() ?? false,
|
|
86
|
+
loadMore: () => list.current?.loadMore(),
|
|
87
|
+
refresh: () => applyFilters(filters),
|
|
88
|
+
applyFilters,
|
|
89
|
+
filters,
|
|
90
|
+
error: error.current,
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export function useGetNotificationTitleAndDescription({ content }: GetTenantNotificationsResponse) {
|
|
95
|
+
const language = useLanguage()
|
|
96
|
+
const { title, description } = content?.[language] || content?.en || {}
|
|
97
|
+
|
|
98
|
+
return {
|
|
99
|
+
title,
|
|
100
|
+
description,
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export function useUnreadNotifications() {
|
|
105
|
+
const [hasUnreadNotifications, setUnreadNotifications] = useState(false)
|
|
106
|
+
const controller = useNotificationController()
|
|
107
|
+
|
|
108
|
+
useEffectOnce(() => {
|
|
109
|
+
const unsubscribe = controller.onUnreadNotificationChange(setUnreadNotifications)
|
|
110
|
+
controller.checkUnread()
|
|
111
|
+
return unsubscribe
|
|
112
|
+
})
|
|
113
|
+
|
|
114
|
+
return hasUnreadNotifications
|
|
115
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
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
|
+
* Marks the notification with the id passed as parameter as unread (uncommitted).
|
|
44
|
+
* @param id the id of the notification.
|
|
45
|
+
*/
|
|
46
|
+
markAsUncommitted(id: string): Promise<void>,
|
|
47
|
+
/**
|
|
48
|
+
* How long (ms) we should wait before checking for unread notifications again.
|
|
49
|
+
* @default 120000
|
|
50
|
+
*/
|
|
51
|
+
pollingMS?: number,
|
|
52
|
+
/**
|
|
53
|
+
* Path to the notifications page.
|
|
54
|
+
* @default '/notifications'
|
|
55
|
+
*/
|
|
56
|
+
notificationsPath?: string,
|
|
57
|
+
/**
|
|
58
|
+
* A function to call whenever an action button of a notification is clicked. It receives the id of the notification as a parameter.
|
|
59
|
+
*
|
|
60
|
+
* Useful for adding a behavior other than committing the notification and redirecting to its action. Example: analytics.
|
|
61
|
+
*
|
|
62
|
+
* @param id the id of the notification.
|
|
63
|
+
*/
|
|
64
|
+
onClickAction?: (id: string) => void,
|
|
65
|
+
/**
|
|
66
|
+
* Function that is called whenever the "read more" button is clicked.
|
|
67
|
+
*/
|
|
68
|
+
onClickViewNotification?: () => void,
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export type UnreadNotificationListener = (hasUnreadNotification: boolean) => void
|