diff --git a/pkg/console/console.go b/pkg/console/console.go index 092e52a1bb8..735802ae7cd 100644 --- a/pkg/console/console.go +++ b/pkg/console/console.go @@ -106,6 +106,7 @@ func generateConsoleCSPString(config *Config, nonce string, others ...webui.Cont ConnectionSource: append([]string{ "'self'", config.UI.SentryDSN, + config.UI.StatusPage, "gravatar.com", "www.gravatar.com", }, baseURLs...), diff --git a/pkg/identityserver/identityserver.go b/pkg/identityserver/identityserver.go index e26e7621961..df39b5fbdaf 100644 --- a/pkg/identityserver/identityserver.go +++ b/pkg/identityserver/identityserver.go @@ -96,6 +96,7 @@ func GenerateCSPString(config *oauth.Config, nonce string) string { ConnectionSource: append([]string{ "'self'", config.UI.SentryDSN, + config.UI.StatusPage, "gravatar.com", "www.gravatar.com", }, baseURLs...), diff --git a/pkg/webui/components/alert-banner/alert-banner.styl b/pkg/webui/components/alert-banner/alert-banner.styl new file mode 100644 index 00000000000..d00f1ba044c --- /dev/null +++ b/pkg/webui/components/alert-banner/alert-banner.styl @@ -0,0 +1,64 @@ +// Copyright © 2024 The Things Network Foundation, The Things Industries B.V. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +.alert-banner + position: absolute + right: 0 + left: 0 + z-index: $zi.slight + padding: $cs.m + opacity: 0 + pointer-events: none + transition: opacity $ad.m + &.visible + opacity: 1 + pointer-events: auto + + .link > span + text-decoration: underline + + .title + line-height: normal + +.error + background-color: var(--c-bg-error-light) + color: var(--c-text-error-bold) + .link, .closeIcon + color: var(--c-text-error-bold) + &:hover + color: var(--c-text-error-normal-hover) + +.success + background-color: var(--c-bg-success-light) + color: var(--c-text-success-bold) + .link, .closeIcon + color: var(--c-text-success-bold) + &:hover + color: var(--c-text-success-normal-hover) + +.warning + background-color: var(--c-bg-warning-light) + color: var(--c-text-warning-bold) + .link, .closeIcon + color: var(--c-text-warning-bold) + &:hover + color: var(--c-text-warning-normal-hover) + +.info + background-color: var(--c-bg-info-light) + color: var(--c-text-info-bold) + .link, .closeIcon + color: var(--c-text-info-bold) + &:hover + color: var(--c-text-info-normal-hover) diff --git a/pkg/webui/components/alert-banner/context.js b/pkg/webui/components/alert-banner/context.js new file mode 100644 index 00000000000..dcf50e57b08 --- /dev/null +++ b/pkg/webui/components/alert-banner/context.js @@ -0,0 +1,84 @@ +// Copyright © 2024 The Things Network Foundation, The Things Industries B.V. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import React, { useContext, useRef, useState } from 'react' + +import AlertBanner from '@ttn-lw/components/alert-banner/index' + +import PropTypes from '@ttn-lw/lib/prop-types' + +const AlertBannerContext = React.createContext() +const { Provider } = AlertBannerContext + +const AlertBannerProvider = ({ children }) => { + const [type, setType] = useState('info') + const [open, setOpen] = useState(false) + const [title, setTitle] = useState('') + const [subtitle, setSubtitle] = useState(undefined) + const [titleValues, setTitleValues] = useState(undefined) + const [subtitleValues, setSubtitleValues] = useState(undefined) + const timeoutRef = useRef(null) + const showBanner = ({ type, title, duration, subtitle, titleValues, subtitleValues }) => { + if (window.innerWidth <= 768) { + clearTimeout(timeoutRef.current) + setType(type) + setTitle(title) + setSubtitle(subtitle) + setTitleValues(titleValues) + setSubtitleValues(subtitleValues) + setOpen(true) + if (duration) { + timeoutRef.current = setTimeout(() => { + setOpen(false) + }, duration) + } + } + } + const closeBanner = () => { + setOpen(false) + clearTimeout(timeoutRef.current) + } + const value = { + showBanner, + } + + return ( + + + {children} + + ) +} + +AlertBannerProvider.propTypes = { + children: PropTypes.node.isRequired, +} + +const useAlertBanner = () => { + const context = useContext(AlertBannerContext) + if (!context) { + throw new Error('useAlertBanner must be used within a AlertBannerProvider') + } + return context +} + +export { AlertBannerProvider, useAlertBanner } diff --git a/pkg/webui/components/alert-banner/index.js b/pkg/webui/components/alert-banner/index.js new file mode 100644 index 00000000000..b55cfb64adf --- /dev/null +++ b/pkg/webui/components/alert-banner/index.js @@ -0,0 +1,104 @@ +// Copyright © 2024 The Things Network Foundation, The Things Industries B.V. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import React, { useEffect, useRef } from 'react' +import classnames from 'classnames' + +import Icon, { IconX } from '@ttn-lw/components/icon' + +import Message from '@ttn-lw/lib/components/message' + +import PropTypes from '@ttn-lw/lib/prop-types' +import from from '@ttn-lw/lib/from' + +import style from './alert-banner.styl' + +const AlertBanner = ({ + className, + type, + open, + title, + subtitle, + titleValues, + subtitleValues, + handleClose, +}) => { + const ref = useRef(null) + + useEffect(() => { + const handleCloseBanner = () => { + if (window.innerWidth > 768 && open) { + handleClose() + } + } + + const header = document.getElementById('header') + if (header) { + const headerHeight = header.offsetHeight + ref.current.style.top = `${headerHeight}px` + } + + window.addEventListener('resize', handleCloseBanner) + return () => { + window.removeEventListener('resize', handleCloseBanner) + } + }, [handleClose, open]) + + return ( +
+
+ + +
+ {subtitle && } +
+ ) +} + +AlertBanner.propTypes = { + className: PropTypes.string, + handleClose: PropTypes.func.isRequired, + open: PropTypes.bool.isRequired, + subtitle: PropTypes.message, + subtitleValues: PropTypes.object, + title: PropTypes.message.isRequired, + titleValues: PropTypes.object, + type: PropTypes.oneOf(['info', 'success', 'warning', 'error']).isRequired, +} + +AlertBanner.defaultProps = { + className: undefined, + subtitle: undefined, + titleValues: undefined, + subtitleValues: undefined, +} +export default AlertBanner diff --git a/pkg/webui/components/alert-banner/story.js b/pkg/webui/components/alert-banner/story.js new file mode 100644 index 00000000000..ba186a21af3 --- /dev/null +++ b/pkg/webui/components/alert-banner/story.js @@ -0,0 +1,62 @@ +// Copyright © 2024 The Things Network Foundation, The Things Industries B.V. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import React from 'react' + +import AlertBanner from '.' + +export default { + title: 'AlertBanner', + component: [AlertBanner], +} + +export const Info = () => ( + {}} + open + /> +) + +export const Warning = () => ( + {}} + open + /> +) + +export const Error = () => ( + {}} + open + /> +) + +export const Success = () => ( + {}} + open + /> +) diff --git a/pkg/webui/components/header/index.js b/pkg/webui/components/header/index.js index 6cdbe14c284..e0abc459abe 100644 --- a/pkg/webui/components/header/index.js +++ b/pkg/webui/components/header/index.js @@ -14,6 +14,7 @@ import React from 'react' import classnames from 'classnames' +import AppStatusBadge from '@console/containers/app-status-badge' import { IconStar, IconPlus, IconInbox, IconMenu2 } from '@ttn-lw/components/icon' import Button from '@ttn-lw/components/button' @@ -36,7 +37,7 @@ const Header = ({ showNotificationDot, ...rest }) => ( -
+