From 28b0654a0b2024f336873d84faf35e33e2cdd64f Mon Sep 17 00:00:00 2001 From: Darya Plotnytska Date: Wed, 2 Aug 2023 12:08:31 +0200 Subject: [PATCH 1/5] console: Refactor application title section --- .../application-title-section.js | 154 ----------------- .../application-title-section/connect.js | 112 ------------ .../application-title-section/index.js | 159 +++++++++++++++++- 3 files changed, 155 insertions(+), 270 deletions(-) delete mode 100644 pkg/webui/console/containers/application-title-section/application-title-section.js delete mode 100644 pkg/webui/console/containers/application-title-section/connect.js diff --git a/pkg/webui/console/containers/application-title-section/application-title-section.js b/pkg/webui/console/containers/application-title-section/application-title-section.js deleted file mode 100644 index 6a4dd9ed14..0000000000 --- a/pkg/webui/console/containers/application-title-section/application-title-section.js +++ /dev/null @@ -1,154 +0,0 @@ -// Copyright © 2020 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 { defineMessages } from 'react-intl' - -import applicationIcon from '@assets/misc/application.svg' - -import Status from '@ttn-lw/components/status' -import DocTooltip from '@ttn-lw/components/tooltip/doc' -import Icon from '@ttn-lw/components/icon' - -import Message from '@ttn-lw/lib/components/message' - -import LastSeen from '@console/components/last-seen' -import EntityTitleSection from '@console/components/entity-title-section' - -import sharedMessages from '@ttn-lw/lib/shared-messages' -import PropTypes from '@ttn-lw/lib/prop-types' - -import style from './application-title-section.styl' - -const m = defineMessages({ - lastSeenAvailableTooltip: - 'The elapsed time since the network registered activity (sent uplinks, confirmed downlinks or (re)join requests) of the end device(s) in this application.', - noActivityTooltip: - 'The network has not recently registered any activity (sent uplinks, confirmed downlinks or (re)join requests) of the end device(s) in this application.', -}) - -const { Content } = EntityTitleSection - -const ApplicationTitleSection = props => { - const { - appId, - fetching, - application, - apiKeysTotalCount, - apiKeysErrored, - collaboratorsTotalCount, - collaboratorsErrored, - devicesTotalCount, - devicesErrored, - lastSeen, - mayViewCollaborators, - mayViewApiKeys, - mayViewDevices, - } = props - - const showLastSeen = Boolean(lastSeen) - - const bottomBarLeft = showLastSeen ? ( - } - > - - - - - ) : ( - } - docPath="/getting-started/console/troubleshooting" - > - - - - - ) - const bottomBarRight = ( - <> - {mayViewDevices && ( - - )} - {mayViewCollaborators && ( - - )} - {mayViewApiKeys && ( - - )} - - ) - - return ( - - - - ) -} - -ApplicationTitleSection.propTypes = { - apiKeysErrored: PropTypes.bool.isRequired, - apiKeysTotalCount: PropTypes.number, - appId: PropTypes.string.isRequired, - application: PropTypes.application.isRequired, - collaboratorsErrored: PropTypes.bool.isRequired, - collaboratorsTotalCount: PropTypes.number, - devicesErrored: PropTypes.bool.isRequired, - devicesTotalCount: PropTypes.number, - fetching: PropTypes.bool.isRequired, - lastSeen: PropTypes.string, - mayViewApiKeys: PropTypes.bool.isRequired, - mayViewCollaborators: PropTypes.bool.isRequired, - mayViewDevices: PropTypes.bool.isRequired, -} - -ApplicationTitleSection.defaultProps = { - apiKeysTotalCount: undefined, - collaboratorsTotalCount: undefined, - devicesTotalCount: undefined, - lastSeen: undefined, -} - -export default ApplicationTitleSection diff --git a/pkg/webui/console/containers/application-title-section/connect.js b/pkg/webui/console/containers/application-title-section/connect.js deleted file mode 100644 index 494cf48446..0000000000 --- a/pkg/webui/console/containers/application-title-section/connect.js +++ /dev/null @@ -1,112 +0,0 @@ -// Copyright © 2020 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 { connect } from 'react-redux' - -import withRequest from '@ttn-lw/lib/components/with-request' - -import { getCollaboratorsList } from '@ttn-lw/lib/store/actions/collaborators' -import { - selectCollaboratorsTotalCount, - selectCollaboratorsFetching, - selectCollaboratorsError, -} from '@ttn-lw/lib/store/selectors/collaborators' - -import { - checkFromState, - mayViewOrEditApplicationApiKeys, - mayViewOrEditApplicationCollaborators, - mayViewApplicationDevices, -} from '@console/lib/feature-checks' - -import { getApiKeysList } from '@console/store/actions/api-keys' -import { getApplicationDeviceCount } from '@console/store/actions/applications' - -import { - selectApplicationById, - selectApplicationDeviceCount, - selectApplicationDeviceCountFetching, - selectApplicationDeviceCountError, - selectApplicationDerivedLastSeen, -} from '@console/store/selectors/applications' -import { - selectApiKeysTotalCount, - selectApiKeysFetching, - selectApiKeysError, -} from '@console/store/selectors/api-keys' - -const mapStateToProps = (state, props) => { - const apiKeysTotalCount = selectApiKeysTotalCount(state) - const apiKeysFetching = selectApiKeysFetching(state) - const apiKeysError = selectApiKeysError(state) - const collaboratorsTotalCount = selectCollaboratorsTotalCount(state, { id: props.appId }) - const collaboratorsFetching = selectCollaboratorsFetching(state) - const collaboratorsError = selectCollaboratorsError(state) - const devicesTotalCount = selectApplicationDeviceCount(state, props.appId) - const devicesFetching = selectApplicationDeviceCountFetching(state) - const devicesError = selectApplicationDeviceCountError(state) - - const fetching = apiKeysFetching || collaboratorsFetching || devicesFetching - - return { - mayViewCollaborators: checkFromState(mayViewOrEditApplicationCollaborators, state), - mayViewApiKeys: checkFromState(mayViewOrEditApplicationApiKeys, state), - mayViewDevices: checkFromState(mayViewApplicationDevices, state), - application: selectApplicationById(state, props.appId), - lastSeen: selectApplicationDerivedLastSeen(state, props.appId), - apiKeysTotalCount, - apiKeysErrored: Boolean(apiKeysError), - collaboratorsTotalCount, - collaboratorsErrored: Boolean(collaboratorsError), - devicesTotalCount, - devicesErrored: Boolean(devicesError), - fetching, - } -} - -const mapDispatchToProps = dispatch => ({ - loadData: (mayViewCollaborators, mayViewApiKeys, mayViewDevices, appId) => { - if (mayViewCollaborators) { - dispatch(getCollaboratorsList('application', appId)) - } - - if (mayViewApiKeys) { - dispatch(getApiKeysList('application', appId)) - } - - if (mayViewDevices) { - dispatch(getApplicationDeviceCount(appId)) - } - }, -}) - -const mergeProps = (stateProps, dispatchProps, ownProps) => ({ - ...stateProps, - ...dispatchProps, - ...ownProps, - loadData: () => - dispatchProps.loadData( - stateProps.mayViewCollaborators, - stateProps.mayViewApiKeys, - stateProps.mayViewDevices, - ownProps.appId, - ), -}) - -export default TitleSection => - connect( - mapStateToProps, - mapDispatchToProps, - mergeProps, - )(withRequest(({ appId, loadData }) => loadData(appId))(TitleSection)) diff --git a/pkg/webui/console/containers/application-title-section/index.js b/pkg/webui/console/containers/application-title-section/index.js index 52bbcb3b7e..1e1fcc9880 100644 --- a/pkg/webui/console/containers/application-title-section/index.js +++ b/pkg/webui/console/containers/application-title-section/index.js @@ -12,9 +12,160 @@ // See the License for the specific language governing permissions and // limitations under the License. -import ApplicationTitleSection from './application-title-section' -import connect from './connect' +import React, { useCallback } from 'react' +import { defineMessages } from 'react-intl' +import { useSelector } from 'react-redux' -const ConnectedApplicationTitleSection = connect(ApplicationTitleSection) +import applicationIcon from '@assets/misc/application.svg' -export { ConnectedApplicationTitleSection as default, ApplicationTitleSection } +import Status from '@ttn-lw/components/status' +import DocTooltip from '@ttn-lw/components/tooltip/doc' +import Icon from '@ttn-lw/components/icon' + +import Message from '@ttn-lw/lib/components/message' +import RequireRequest from '@ttn-lw/lib/components/require-request' + +import LastSeen from '@console/components/last-seen' +import EntityTitleSection from '@console/components/entity-title-section' + +import sharedMessages from '@ttn-lw/lib/shared-messages' +import PropTypes from '@ttn-lw/lib/prop-types' +import { selectCollaboratorsTotalCount } from '@ttn-lw/lib/store/selectors/collaborators' +import { getCollaboratorsList } from '@ttn-lw/lib/store/actions/collaborators' + +import { + checkFromState, + mayViewOrEditApplicationApiKeys, + mayViewOrEditApplicationCollaborators, + mayViewApplicationDevices, +} from '@console/lib/feature-checks' + +import { getApiKeysList } from '@console/store/actions/api-keys' +import { getApplicationDeviceCount } from '@console/store/actions/applications' + +import { + selectApplicationById, + selectApplicationDeviceCount, + selectApplicationDerivedLastSeen, +} from '@console/store/selectors/applications' +import { selectApiKeysTotalCount } from '@console/store/selectors/api-keys' + +import style from './application-title-section.styl' + +const m = defineMessages({ + lastSeenAvailableTooltip: + 'The elapsed time since the network registered activity (sent uplinks, confirmed downlinks or (re)join requests) of the end device(s) in this application.', + noActivityTooltip: + 'The network has not recently registered any activity (sent uplinks, confirmed downlinks or (re)join requests) of the end device(s) in this application.', +}) + +const { Content } = EntityTitleSection + +const ApplicationTitleSection = ({ appId }) => { + const apiKeysTotalCount = useSelector(selectApiKeysTotalCount) + const collaboratorsTotalCount = useSelector(state => + selectCollaboratorsTotalCount(state, { id: appId }), + ) + const devicesTotalCount = useSelector(state => selectApplicationDeviceCount(state, appId)) + const application = useSelector(state => selectApplicationById(state, appId)) + const lastSeen = useSelector(selectApplicationDerivedLastSeen) + const mayViewCollaborators = useSelector(state => + checkFromState(mayViewOrEditApplicationCollaborators, state), + ) + const mayViewApiKeys = useSelector(state => + checkFromState(mayViewOrEditApplicationApiKeys, state), + ) + const mayViewDevices = useSelector(state => checkFromState(mayViewApplicationDevices, state)) + + const showLastSeen = Boolean(lastSeen) + + const bottomBarLeft = showLastSeen ? ( + } + > + + + + + ) : ( + } + docPath="/getting-started/console/troubleshooting" + > + + + + + ) + const bottomBarRight = ( + <> + {mayViewDevices && ( + + )} + {mayViewCollaborators && ( + + )} + {mayViewApiKeys && ( + + )} + + ) + + const loadData = useCallback( + async dispatch => { + if (mayViewCollaborators) { + dispatch(getCollaboratorsList('application', appId)) + } + + if (mayViewApiKeys) { + dispatch(getApiKeysList('application', appId)) + } + + if (mayViewDevices) { + dispatch(getApplicationDeviceCount(appId)) + } + }, + [appId, mayViewApiKeys, mayViewCollaborators, mayViewDevices], + ) + + return ( + + + + + + ) +} + +ApplicationTitleSection.propTypes = { + appId: PropTypes.string.isRequired, +} + +export default ApplicationTitleSection From 295c19ce1d475ae56cb1dc7d3a87b73b8b504c59 Mon Sep 17 00:00:00 2001 From: Darya Plotnytska Date: Wed, 2 Aug 2023 12:09:10 +0200 Subject: [PATCH 2/5] console: Refactor gateway title section --- .../gateway-title-section/connect.js | 92 ---------------- .../gateway-title-section.js | 99 ----------------- .../containers/gateway-title-section/index.js | 100 +++++++++++++++++- 3 files changed, 96 insertions(+), 195 deletions(-) delete mode 100644 pkg/webui/console/containers/gateway-title-section/connect.js delete mode 100644 pkg/webui/console/containers/gateway-title-section/gateway-title-section.js diff --git a/pkg/webui/console/containers/gateway-title-section/connect.js b/pkg/webui/console/containers/gateway-title-section/connect.js deleted file mode 100644 index 9149206d79..0000000000 --- a/pkg/webui/console/containers/gateway-title-section/connect.js +++ /dev/null @@ -1,92 +0,0 @@ -// Copyright © 2020 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 { connect } from 'react-redux' - -import withRequest from '@ttn-lw/lib/components/with-request' - -import { getCollaboratorsList } from '@ttn-lw/lib/store/actions/collaborators' -import { - selectCollaboratorsTotalCount, - selectCollaboratorsFetching, - selectCollaboratorsError, -} from '@ttn-lw/lib/store/selectors/collaborators' - -import { - checkFromState, - mayViewOrEditGatewayApiKeys, - mayViewOrEditGatewayCollaborators, -} from '@console/lib/feature-checks' - -import { getApiKeysList } from '@console/store/actions/api-keys' - -import { selectGatewayById } from '@console/store/selectors/gateways' -import { - selectApiKeysTotalCount, - selectApiKeysFetching, - selectApiKeysError, -} from '@console/store/selectors/api-keys' - -const mapStateToProps = (state, props) => { - const apiKeysTotalCount = selectApiKeysTotalCount(state) - const apiKeysFetching = selectApiKeysFetching(state) - const apiKeysError = selectApiKeysError(state) - const collaboratorsTotalCount = selectCollaboratorsTotalCount(state) - const collaboratorsFetching = selectCollaboratorsFetching(state) - const collaboratorsError = selectCollaboratorsError(state) - - const fetching = apiKeysFetching || collaboratorsFetching - - return { - mayViewCollaborators: checkFromState(mayViewOrEditGatewayCollaborators, state), - mayViewApiKeys: checkFromState(mayViewOrEditGatewayApiKeys, state), - gateway: selectGatewayById(state, props.gtwId), - apiKeysTotalCount, - apiKeysErrored: Boolean(apiKeysError), - collaboratorsTotalCount, - collaboratorsErrored: Boolean(collaboratorsError), - fetching, - } -} - -const mapDispatchToProps = dispatch => ({ - loadData: (mayViewCollaborators, mayViewApiKeys, gtwId) => { - if (mayViewCollaborators) { - dispatch(getCollaboratorsList('gateway', gtwId)) - } - - if (mayViewApiKeys) { - dispatch(getApiKeysList('gateway', gtwId)) - } - }, -}) - -const mergeProps = (stateProps, dispatchProps, ownProps) => ({ - ...stateProps, - ...dispatchProps, - ...ownProps, - loadData: () => - dispatchProps.loadData( - stateProps.mayViewCollaborators, - stateProps.mayViewApiKeys, - ownProps.gtwId, - ), -}) - -export default TitleSection => - connect( - mapStateToProps, - mapDispatchToProps, - mergeProps, - )(withRequest(({ loadData }) => loadData())(TitleSection)) diff --git a/pkg/webui/console/containers/gateway-title-section/gateway-title-section.js b/pkg/webui/console/containers/gateway-title-section/gateway-title-section.js deleted file mode 100644 index bddb6f4040..0000000000 --- a/pkg/webui/console/containers/gateway-title-section/gateway-title-section.js +++ /dev/null @@ -1,99 +0,0 @@ -// Copyright © 2020 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 gatewayIcon from '@assets/misc/gateway.svg' - -import EntityTitleSection from '@console/components/entity-title-section' - -import GatewayConnection from '@console/containers/gateway-connection' - -import sharedMessages from '@ttn-lw/lib/shared-messages' -import PropTypes from '@ttn-lw/lib/prop-types' - -const { Content } = EntityTitleSection - -const GatewayTitleSection = props => { - const { - fetching, - gtwId, - gateway, - apiKeysTotalCount, - apiKeysErrored, - collaboratorsTotalCount, - collaboratorsErrored, - mayViewCollaborators, - mayViewApiKeys, - } = props - - const bottomBarLeft = - const bottomBarRight = ( - <> - {mayViewCollaborators && ( - - )} - {mayViewApiKeys && ( - - )} - - ) - - return ( - - - - ) -} - -GatewayTitleSection.propTypes = { - apiKeysErrored: PropTypes.bool.isRequired, - apiKeysTotalCount: PropTypes.number, - collaboratorsErrored: PropTypes.bool.isRequired, - collaboratorsTotalCount: PropTypes.number, - fetching: PropTypes.bool.isRequired, - gateway: PropTypes.gateway.isRequired, - gtwId: PropTypes.string.isRequired, - mayViewApiKeys: PropTypes.bool.isRequired, - mayViewCollaborators: PropTypes.bool.isRequired, -} - -GatewayTitleSection.defaultProps = { - apiKeysTotalCount: undefined, - collaboratorsTotalCount: undefined, -} - -export default GatewayTitleSection diff --git a/pkg/webui/console/containers/gateway-title-section/index.js b/pkg/webui/console/containers/gateway-title-section/index.js index 7b95fc5656..4ae4309045 100644 --- a/pkg/webui/console/containers/gateway-title-section/index.js +++ b/pkg/webui/console/containers/gateway-title-section/index.js @@ -12,9 +12,101 @@ // See the License for the specific language governing permissions and // limitations under the License. -import GatewayTitleSection from './gateway-title-section' -import connect from './connect' +import React, { useCallback } from 'react' +import { useSelector } from 'react-redux' -const ConnectedGatewayTitleSection = connect(GatewayTitleSection) +import gatewayIcon from '@assets/misc/gateway.svg' -export { ConnectedGatewayTitleSection as default, GatewayTitleSection } +import RequireRequest from '@ttn-lw/lib/components/require-request' + +import EntityTitleSection from '@console/components/entity-title-section' + +import GatewayConnection from '@console/containers/gateway-connection' + +import sharedMessages from '@ttn-lw/lib/shared-messages' +import PropTypes from '@ttn-lw/lib/prop-types' +import { selectCollaboratorsTotalCount } from '@ttn-lw/lib/store/selectors/collaborators' +import { getCollaboratorsList } from '@ttn-lw/lib/store/actions/collaborators' + +import { + checkFromState, + mayViewOrEditGatewayApiKeys, + mayViewOrEditGatewayCollaborators, +} from '@console/lib/feature-checks' + +import { getApiKeysList } from '@console/store/actions/api-keys' + +import { selectApiKeysTotalCount } from '@console/store/selectors/api-keys' +import { selectGatewayById } from '@console/store/selectors/gateways' + +const { Content } = EntityTitleSection + +const GatewayTitleSection = ({ gtwId }) => { + const apiKeysTotalCount = useSelector(selectApiKeysTotalCount) + const collaboratorsTotalCount = useSelector(state => + selectCollaboratorsTotalCount(state, { id: gtwId }), + ) + const mayViewCollaborators = useSelector(state => + checkFromState(mayViewOrEditGatewayCollaborators, state), + ) + const mayViewApiKeys = useSelector(state => checkFromState(mayViewOrEditGatewayApiKeys, state)) + const gateway = useSelector(state => selectGatewayById(state, gtwId)) + + const bottomBarLeft = + const bottomBarRight = ( + <> + {mayViewCollaborators && ( + + )} + {mayViewApiKeys && ( + + )} + + ) + + const loadData = useCallback( + async dispatch => { + if (mayViewCollaborators) { + dispatch(getCollaboratorsList('gateway', gtwId)) + } + + if (mayViewApiKeys) { + dispatch(getApiKeysList('gateway', gtwId)) + } + }, + [gtwId, mayViewApiKeys, mayViewCollaborators], + ) + + return ( + + + + + + ) +} + +GatewayTitleSection.propTypes = { + gtwId: PropTypes.string.isRequired, +} + +export default GatewayTitleSection From 8f0c8c1cefbef8138e0ff6ac0d1a32185ffcf719 Mon Sep 17 00:00:00 2001 From: Darya Plotnytska Date: Wed, 2 Aug 2023 12:09:37 +0200 Subject: [PATCH 3/5] console: Refactor organization title section --- .../organization-title-section/connect.js | 92 ----------------- .../organization-title-section/index.js | 91 ++++++++++++++++- .../organization-title-section.js | 99 ------------------- 3 files changed, 87 insertions(+), 195 deletions(-) delete mode 100644 pkg/webui/console/containers/organization-title-section/connect.js delete mode 100644 pkg/webui/console/containers/organization-title-section/organization-title-section.js diff --git a/pkg/webui/console/containers/organization-title-section/connect.js b/pkg/webui/console/containers/organization-title-section/connect.js deleted file mode 100644 index b6ac5970e6..0000000000 --- a/pkg/webui/console/containers/organization-title-section/connect.js +++ /dev/null @@ -1,92 +0,0 @@ -// Copyright © 2020 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 { connect } from 'react-redux' - -import withRequest from '@ttn-lw/lib/components/with-request' - -import { getCollaboratorsList } from '@ttn-lw/lib/store/actions/collaborators' -import { - selectCollaboratorsTotalCount, - selectCollaboratorsFetching, - selectCollaboratorsError, -} from '@ttn-lw/lib/store/selectors/collaborators' - -import { - checkFromState, - mayViewOrEditOrganizationApiKeys, - mayViewOrEditOrganizationCollaborators, -} from '@console/lib/feature-checks' - -import { getApiKeysList } from '@console/store/actions/api-keys' - -import { selectOrganizationById } from '@console/store/selectors/organizations' -import { - selectApiKeysTotalCount, - selectApiKeysFetching, - selectApiKeysError, -} from '@console/store/selectors/api-keys' - -const mapStateToProps = (state, props) => { - const apiKeysTotalCount = selectApiKeysTotalCount(state) - const apiKeysFetching = selectApiKeysFetching(state) - const apiKeysError = selectApiKeysError(state) - const collaboratorsTotalCount = selectCollaboratorsTotalCount(state, { id: props.appId }) - const collaboratorsFetching = selectCollaboratorsFetching(state) - const collaboratorsError = selectCollaboratorsError(state) - - const fetching = apiKeysFetching || collaboratorsFetching - - return { - mayViewCollaborators: checkFromState(mayViewOrEditOrganizationCollaborators, state), - mayViewApiKeys: checkFromState(mayViewOrEditOrganizationApiKeys, state), - organization: selectOrganizationById(state, props.orgId), - apiKeysTotalCount, - apiKeysErrored: Boolean(apiKeysError), - collaboratorsTotalCount, - collaboratorsErrored: Boolean(collaboratorsError), - fetching, - } -} - -const mapDispatchToProps = dispatch => ({ - loadData: (mayViewCollaborators, mayViewApiKeys, orgId) => { - if (mayViewCollaborators) { - dispatch(getCollaboratorsList('organization', orgId)) - } - - if (mayViewApiKeys) { - dispatch(getApiKeysList('organization', orgId)) - } - }, -}) - -const mergeProps = (stateProps, dispatchProps, ownProps) => ({ - ...stateProps, - ...dispatchProps, - ...ownProps, - loadData: () => - dispatchProps.loadData( - stateProps.mayViewCollaborators, - stateProps.mayViewApiKeys, - ownProps.orgId, - ), -}) - -export default TitleSection => - connect( - mapStateToProps, - mapDispatchToProps, - mergeProps, - )(withRequest(({ loadData }) => loadData())(TitleSection)) diff --git a/pkg/webui/console/containers/organization-title-section/index.js b/pkg/webui/console/containers/organization-title-section/index.js index 3342656f70..5bceb425e9 100644 --- a/pkg/webui/console/containers/organization-title-section/index.js +++ b/pkg/webui/console/containers/organization-title-section/index.js @@ -12,9 +12,92 @@ // See the License for the specific language governing permissions and // limitations under the License. -import OrganizationTitleSection from './organization-title-section' -import connect from './connect' +import React, { useCallback } from 'react' +import { useSelector } from 'react-redux' -const ConnectedOrganizationTitleSection = connect(OrganizationTitleSection) +import organizationIcon from '@assets/misc/organization.svg' -export { ConnectedOrganizationTitleSection as default, OrganizationTitleSection } +import RequireRequest from '@ttn-lw/lib/components/require-request' + +import EntityTitleSection from '@console/components/entity-title-section' + +import sharedMessages from '@ttn-lw/lib/shared-messages' +import PropTypes from '@ttn-lw/lib/prop-types' +import { selectCollaboratorsTotalCount } from '@ttn-lw/lib/store/selectors/collaborators' +import { getCollaboratorsList } from '@ttn-lw/lib/store/actions/collaborators' + +import { + checkFromState, + mayViewOrEditOrganizationApiKeys, + mayViewOrEditOrganizationCollaborators, +} from '@console/lib/feature-checks' + +import { getApiKeysList } from '@console/store/actions/api-keys' + +import { selectApiKeysTotalCount } from '@console/store/selectors/api-keys' +import { selectOrganizationById } from '@console/store/selectors/organizations' + +const { Content } = EntityTitleSection + +const OrganizationTitleSection = ({ orgId }) => { + const apiKeysTotalCount = useSelector(selectApiKeysTotalCount) + const collaboratorsTotalCount = useSelector(state => + selectCollaboratorsTotalCount(state, { id: orgId }), + ) + const mayViewCollaborators = useSelector(state => + checkFromState(mayViewOrEditOrganizationCollaborators, state), + ) + const mayViewApiKeys = useSelector(state => + checkFromState(mayViewOrEditOrganizationApiKeys, state), + ) + const organization = useSelector(state => selectOrganizationById(state, orgId)) + + const loadData = useCallback( + async dispatch => { + if (mayViewCollaborators) { + dispatch(getCollaboratorsList('organization', orgId)) + } + + if (mayViewApiKeys) { + dispatch(getApiKeysList('organization', orgId)) + } + }, + [mayViewApiKeys, mayViewCollaborators, orgId], + ) + + return ( + + + + {mayViewCollaborators && ( + + )} + {mayViewApiKeys && ( + + )} + + + + ) +} + +OrganizationTitleSection.propTypes = { + orgId: PropTypes.string.isRequired, +} + +export default OrganizationTitleSection diff --git a/pkg/webui/console/containers/organization-title-section/organization-title-section.js b/pkg/webui/console/containers/organization-title-section/organization-title-section.js deleted file mode 100644 index e634c8cd72..0000000000 --- a/pkg/webui/console/containers/organization-title-section/organization-title-section.js +++ /dev/null @@ -1,99 +0,0 @@ -// Copyright © 2020 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 organizationIcon from '@assets/misc/organization.svg' - -import Spinner from '@ttn-lw/components/spinner' - -import Message from '@ttn-lw/lib/components/message' - -import EntityTitleSection from '@console/components/entity-title-section' - -import sharedMessages from '@ttn-lw/lib/shared-messages' -import PropTypes from '@ttn-lw/lib/prop-types' - -const { Content } = EntityTitleSection - -const OrganizationTitleSection = props => { - const { - fetching, - orgId, - organization, - apiKeysTotalCount, - apiKeysErrored, - collaboratorsTotalCount, - collaboratorsErrored, - mayViewCollaborators, - mayViewApiKeys, - } = props - - return ( - - - {fetching ? ( - - - - ) : ( - <> - {mayViewCollaborators && ( - - )} - {mayViewApiKeys && ( - - )} - - )} - - - ) -} - -OrganizationTitleSection.propTypes = { - apiKeysErrored: PropTypes.bool.isRequired, - apiKeysTotalCount: PropTypes.number, - collaboratorsErrored: PropTypes.bool.isRequired, - collaboratorsTotalCount: PropTypes.number, - fetching: PropTypes.bool.isRequired, - mayViewApiKeys: PropTypes.bool.isRequired, - mayViewCollaborators: PropTypes.bool.isRequired, - orgId: PropTypes.string.isRequired, - organization: PropTypes.organization.isRequired, -} - -OrganizationTitleSection.defaultProps = { - apiKeysTotalCount: undefined, - collaboratorsTotalCount: undefined, -} - -export default OrganizationTitleSection From e6e6a01c1c79b7adfef082634512f5373279ea4a Mon Sep 17 00:00:00 2001 From: Darya Plotnytska Date: Wed, 2 Aug 2023 12:09:49 +0200 Subject: [PATCH 4/5] dev: Update locales --- pkg/webui/locales/en.json | 98 ++++++++++++++++++--------------------- 1 file changed, 45 insertions(+), 53 deletions(-) diff --git a/pkg/webui/locales/en.json b/pkg/webui/locales/en.json index 60a8e14125..8273c210af 100644 --- a/pkg/webui/locales/en.json +++ b/pkg/webui/locales/en.json @@ -361,8 +361,6 @@ "console.components.payload-formatters-form.index.learnMoreAboutPayloadFormatters": "Learn more about payload formatters", "console.components.payload-formatters-form.index.learnMoreAboutCayenne": "What is CayenneLPP?", "console.components.payload-formatters-form.index.noRepositoryWarning": "The application formatter is set to `Repository` but this device does not have an associated formatter in the LoRaWAN Device repository. Messages for this end device will hence not be formatted.", - "console.components.payload-formatters-form.index.confirmNavigationTitle": "Confirm navigation", - "console.components.payload-formatters-form.index.confirmNavigationMessage": "Are you sure you want to leave this page? Your current changes have not been saved yet.", "console.components.payload-formatters-form.test-form.index.validResult": "Payload is valid", "console.components.payload-formatters-form.test-form.index.noResult": "No test result generated yet", "console.components.payload-formatters-form.test-form.index.testDecoder": "Test decoder", @@ -418,6 +416,20 @@ "console.components.uplink-form.uplink-form.simulateUplink": "Simulate uplink", "console.components.uplink-form.uplink-form.payloadDescription": "The desired payload bytes of the uplink message", "console.components.uplink-form.uplink-form.uplinkSuccess": "Uplink sent", + "console.components.user-data-form.index.adminLabel": "Grant this user admin status", + "console.components.user-data-form.index.adminDescription": "Admin status enables overarching rights such as managing other users or modifying entities regardless of collaboration status", + "console.components.user-data-form.index.userDescPlaceholder": "Description for my new user", + "console.components.user-data-form.index.userDescDescription": "Optional user description; can also be used to save notes about the user", + "console.components.user-data-form.index.userIdPlaceholder": "jane-doe", + "console.components.user-data-form.index.userNamePlaceholder": "Jane Doe", + "console.components.user-data-form.index.emailPlaceholder": "mail@example.com", + "console.components.user-data-form.index.emailAddressDescription": "Primary email address used for logging in; this address is not publicly visible", + "console.components.user-data-form.index.emailAddressValidation": "Treat email address as validated", + "console.components.user-data-form.index.emailAddressValidationDescription": "Enable this option if you do not need this user to validate the email address", + "console.components.user-data-form.index.deleteTitle": "Are you sure you want to delete this account?", + "console.components.user-data-form.index.deleteWarning": "This will PERMANENTLY DELETE THIS ACCOUNT and LOCK THE USER ID AND EMAIL FOR RE-REGISTRATION. Associated entities (e.g. gateways, applications and end devices) owned by this user that do not have any other collaborators will become UNACCESSIBLE and it will NOT BE POSSIBLE TO REGISTER ENTITIES WITH THE SAME ID OR EUI's AGAIN. Make sure you assign new collaborators to such entities if you plan to continue using them.", + "console.components.user-data-form.index.purgeWarning": "This will PERMANENTLY DELETE THIS ACCOUNT. Associated entities (e.g. gateways, applications and end devices) owned by this user that do not have any other collaborators will become UNACCESSIBLE and it will NOT BE POSSIBLE TO REGISTER ENTITIES WITH THE SAME ID OR EUI's AGAIN. Make sure you assign new collaborators to such entities if you plan to continue using them.", + "console.components.user-data-form.index.deleteConfirmMessage": "Please type in this user's user ID to confirm.", "console.components.webhook-form.index.idPlaceholder": "my-new-webhook", "console.components.webhook-form.index.messageInfo": "For each enabled event type an optional path can be defined which will be appended to the base URL", "console.components.webhook-form.index.deleteWebhook": "Delete Webhook", @@ -468,6 +480,8 @@ "console.containers.application-payload-formatters.uplink.uplinkResetWarning": "You do not have sufficient rights to view the current uplink payload formatter. Only overwriting is allowed.", "console.containers.application-title-section.application-title-section.lastSeenAvailableTooltip": "The elapsed time since the network registered activity (sent uplinks, confirmed downlinks or (re)join requests) of the end device(s) in this application.", "console.containers.application-title-section.application-title-section.noActivityTooltip": "The network has not recently registered any activity (sent uplinks, confirmed downlinks or (re)join requests) of the end device(s) in this application.", + "console.containers.application-title-section.index.lastSeenAvailableTooltip": "The elapsed time since the network registered activity (sent uplinks, confirmed downlinks or (re)join requests) of the end device(s) in this application.", + "console.containers.application-title-section.index.noActivityTooltip": "The network has not recently registered any activity (sent uplinks, confirmed downlinks or (re)join requests) of the end device(s) in this application.", "console.containers.applications-form.index.applicationName": "Application name", "console.containers.applications-form.index.appIdPlaceholder": "my-new-application", "console.containers.applications-form.index.appNamePlaceholder": "My new application", @@ -487,31 +501,31 @@ "console.containers.deployment-component-status.index.statusPage": "Go to status page", "console.containers.deployment-component-status.index.seeChangelog": "See changelog", "console.containers.dev-addr-input.index.devAddrFetchingFailure": "There was an error and the end device address could not be generated", - "console.containers.device-importer.messages.proceed": "Proceed to end device list", - "console.containers.device-importer.messages.retry": "Retry from scratch", - "console.containers.device-importer.messages.abort": "Abort", - "console.containers.device-importer.messages.converting": "Converting templates…", - "console.containers.device-importer.messages.creating": "Creating end devices…", - "console.containers.device-importer.messages.operationInProgress": "Operation in progress", - "console.containers.device-importer.messages.operationHalted": "Operation halted", - "console.containers.device-importer.messages.operationFinished": "Operation finished", - "console.containers.device-importer.messages.operationAborted": "Operation aborted", - "console.containers.device-importer.messages.errorTitle": "There was an error and the operation could not be completed", - "console.containers.device-importer.messages.conversionErrorTitle": "Could not import devices", - "console.containers.device-importer.messages.conversionErrorMessage": "An error occurred while processing the provided end device template. This could be due to invalid format, syntax or file encoding. Please check the provided template file and try again. See also our documentation on Importing End Devices for more information.", - "console.containers.device-importer.messages.incompleteWarningTitle": "Not all devices imported successfully", - "console.containers.device-importer.messages.incompleteWarningMessage": "{count, number} {count, plural, one {end device} other {end devices}} could not be imported successfully, because {count, plural, one {its} other {their}} registration attempt resulted in an error", - "console.containers.device-importer.messages.incompleteStatus": "The registration of the following {count, plural, one {end device} other {end devices}} failed:", - "console.containers.device-importer.messages.noneWarningTitle": "No end device was created", - "console.containers.device-importer.messages.noneWarningMessage": "None of your specified end devices was imported, because each registration attempt resulted in an error", - "console.containers.device-importer.messages.processLog": "Process log", - "console.containers.device-importer.messages.progress": "Successfully converted {errorCount, number} of {deviceCount, number} {deviceCount, plural, one {end device} other {end devices}}", - "console.containers.device-importer.messages.successInfoTitle": "All end devices imported successfully", - "console.containers.device-importer.messages.successInfoMessage": "All of the specified end devices have been converted and imported successfully", - "console.containers.device-importer.messages.documentationHint": "Please also see our documentation on Importing End Devices for more information and possible resolutions.", - "console.containers.device-importer.messages.abortWarningTitle": "Device import aborted", - "console.containers.device-importer.messages.abortWarningMessage": "The end device import was aborted and the remaining {count, number} {count, plural, one {end device} other {end devices}} have not been imported", - "console.containers.device-importer.messages.largeFileWarningMessage": "Providing files larger than {warningThreshold} can cause issues during the import process. We recommend you to split such files up into multiple smaller files and importing them one by one.", + "console.containers.device-importer.index.proceed": "Proceed to end device list", + "console.containers.device-importer.index.retry": "Retry from scratch", + "console.containers.device-importer.index.abort": "Abort", + "console.containers.device-importer.index.converting": "Converting templates…", + "console.containers.device-importer.index.creating": "Creating end devices…", + "console.containers.device-importer.index.operationInProgress": "Operation in progress", + "console.containers.device-importer.index.operationHalted": "Operation halted", + "console.containers.device-importer.index.operationFinished": "Operation finished", + "console.containers.device-importer.index.operationAborted": "Operation aborted", + "console.containers.device-importer.index.errorTitle": "There was an error and the operation could not be completed", + "console.containers.device-importer.index.conversionErrorTitle": "Could not import devices", + "console.containers.device-importer.index.conversionErrorMessage": "An error occurred while processing the provided end device template. This could be due to invalid format, syntax or file encoding. Please check the provided template file and try again. See also our documentation on Importing End Devices for more information.", + "console.containers.device-importer.index.incompleteWarningTitle": "Not all devices imported successfully", + "console.containers.device-importer.index.incompleteWarningMessage": "{count, number} {count, plural, one {end device} other {end devices}} could not be imported successfully, because {count, plural, one {its} other {their}} registration attempt resulted in an error", + "console.containers.device-importer.index.incompleteStatus": "The registration of the following {count, plural, one {end device} other {end devices}} failed:", + "console.containers.device-importer.index.noneWarningTitle": "No end device was created", + "console.containers.device-importer.index.noneWarningMessage": "None of your specified end devices was imported, because each registration attempt resulted in an error", + "console.containers.device-importer.index.processLog": "Process log", + "console.containers.device-importer.index.progress": "Successfully converted {errorCount, number} of {deviceCount, number} {deviceCount, plural, one {end device} other {end devices}}", + "console.containers.device-importer.index.successInfoTitle": "All end devices imported successfully", + "console.containers.device-importer.index.successInfoMessage": "All of the specified end devices have been converted and imported successfully", + "console.containers.device-importer.index.documentationHint": "Please also see our documentation on Importing End Devices for more information and possible resolutions.", + "console.containers.device-importer.index.abortWarningTitle": "Device import aborted", + "console.containers.device-importer.index.abortWarningMessage": "The end device import was aborted and the remaining {count, number} {count, plural, one {end device} other {end devices}} have not been imported", + "console.containers.device-importer.index.largeFileWarningMessage": "Providing files larger than {warningThreshold} can cause issues during the import process. We recommend you to split such files up into multiple smaller files and importing them one by one.", "console.containers.device-onboarding-form.messages.endDeviceType": "End device type", "console.containers.device-onboarding-form.messages.provisioningTitle": "Provisioning information", "console.containers.device-onboarding-form.messages.inputMethod": "Input Method", @@ -646,6 +660,8 @@ "console.containers.lora-cloud-gls-form.index.multiFrameTimeWindow": "Multiframe time window", "console.containers.lora-cloud-gls-form.index.multiFrameTimeWindowDescription": "The maximum age of considered historical messages in minutes", "console.containers.lora-cloud-gls-form.index.enableMultiFrame": "Enable multiframe", + "console.containers.move-away-modal.move-away-modal.modalTitle": "Confirm navigation", + "console.containers.move-away-modal.move-away-modal.modalMessage": "Are you sure you want to leave this page? Your current changes have not been saved yet.", "console.containers.network-information-container.index.openSourceInfo": "You are currently using The Things Stack Open Source. More features can be unlocked by using The Things Stack Cloud.", "console.containers.network-information-container.index.plansButton": "Get started with The things Stack Cloud", "console.containers.network-information-container.registry-totals.applicationsUsed": "Total applications", @@ -675,32 +691,6 @@ "console.containers.pubsub-formats-select.index.warning": "Pub/Sub formats unavailable", "console.containers.pubsubs-table.index.format": "Format", "console.containers.pubsubs-table.index.host": "Server host", - "console.containers.user-data-form.add.adminLabel": "Grant this user admin status", - "console.containers.user-data-form.add.adminDescription": "Admin status enables overarching rights such as managing other users or modifying entities regardless of collaboration status", - "console.containers.user-data-form.add.userDescPlaceholder": "Description for my new user", - "console.containers.user-data-form.add.userDescDescription": "Optional user description; can also be used to save notes about the user", - "console.containers.user-data-form.add.userIdPlaceholder": "jane-doe", - "console.containers.user-data-form.add.userNamePlaceholder": "Jane Doe", - "console.containers.user-data-form.add.emailPlaceholder": "mail@example.com", - "console.containers.user-data-form.add.emailAddressDescription": "Primary email address used for logging in; this address is not publicly visible", - "console.containers.user-data-form.add.emailAddressValidation": "Treat email address as validated", - "console.containers.user-data-form.add.emailAddressValidationDescription": "Enable this option if you do not need this user to validate the email address", - "console.containers.user-data-form.edit.adminLabel": "Grant this user admin status", - "console.containers.user-data-form.edit.adminDescription": "Admin status enables overarching rights such as managing other users or modifying entities regardless of collaboration status", - "console.containers.user-data-form.edit.userDescPlaceholder": "Description for my new user", - "console.containers.user-data-form.edit.userDescDescription": "Optional user description; can also be used to save notes about the user", - "console.containers.user-data-form.edit.userIdPlaceholder": "jane-doe", - "console.containers.user-data-form.edit.userNamePlaceholder": "Jane Doe", - "console.containers.user-data-form.edit.emailPlaceholder": "mail@example.com", - "console.containers.user-data-form.edit.emailAddressDescription": "Primary email address used for logging in; this address is not publicly visible", - "console.containers.user-data-form.edit.emailAddressValidation": "Treat email address as validated", - "console.containers.user-data-form.edit.emailAddressValidationDescription": "Enable this option if you do not need this user to validate the email address", - "console.containers.user-data-form.edit.deleteTitle": "Are you sure you want to delete this account?", - "console.containers.user-data-form.edit.deleteWarning": "This will PERMANENTLY DELETE THIS ACCOUNT and LOCK THE USER ID AND EMAIL FOR RE-REGISTRATION. Associated entities (e.g. gateways, applications and end devices) owned by this user that do not have any other collaborators will become UNACCESSIBLE and it will NOT BE POSSIBLE TO REGISTER ENTITIES WITH THE SAME ID OR EUI's AGAIN. Make sure you assign new collaborators to such entities if you plan to continue using them.", - "console.containers.user-data-form.edit.purgeWarning": "This will PERMANENTLY DELETE THIS ACCOUNT. Associated entities (e.g. gateways, applications and end devices) owned by this user that do not have any other collaborators will become UNACCESSIBLE and it will NOT BE POSSIBLE TO REGISTER ENTITIES WITH THE SAME ID OR EUI's AGAIN. Make sure you assign new collaborators to such entities if you plan to continue using them.", - "console.containers.user-data-form.edit.deleteConfirmMessage": "Please type in this user's user ID to confirm.", - "console.containers.user-data-form.edit.updateSuccess": "User updated", - "console.containers.user-data-form.edit.deleteSuccess": "User deleted", "console.containers.users-table.index.invite": "Invite user", "console.containers.users-table.index.revokeInvitation": "Revoke this invitation", "console.containers.users-table.index.sentAt": "Sent", @@ -838,6 +828,8 @@ "console.views.admin-packet-broker.messages.defaultGatewayVisibilitySet": "Default gateway visibility set", "console.views.admin-packet-broker.messages.packetBrokerStatusPage": "Packet Broker Status page", "console.views.admin-panel-network-information.index.title": "Network information", + "console.views.admin-user-management-edit.index.updateSuccess": "User updated", + "console.views.admin-user-management-edit.index.deleteSuccess": "User deleted", "console.views.application-add.index.appDescription": "Within applications, you can register and manage end devices and their network data. After setting up your device fleet, use one of our many integration options to pass relevant data to your external services.{break}Learn more in our guide on Adding Applications.", "console.views.application-data.index.appData": "Application data", "console.views.application-integrations-lora-cloud.index.loraCloudInfoText": "Lora Cloud provides value added APIs that enable simple solutions for common tasks related to LoRaWAN networks and LoRa-based devices. You can setup our LoRaCloud integrations below.", From 87c4e2ca4850d52d75e22e146722cc5fa659c9d5 Mon Sep 17 00:00:00 2001 From: Darya Plotnytska Date: Wed, 2 Aug 2023 14:50:21 +0200 Subject: [PATCH 5/5] console: Fix locales --- pkg/webui/locales/en.json | 98 +++++++++++++++++++++------------------ 1 file changed, 53 insertions(+), 45 deletions(-) diff --git a/pkg/webui/locales/en.json b/pkg/webui/locales/en.json index 8273c210af..0be19fb025 100644 --- a/pkg/webui/locales/en.json +++ b/pkg/webui/locales/en.json @@ -361,6 +361,8 @@ "console.components.payload-formatters-form.index.learnMoreAboutPayloadFormatters": "Learn more about payload formatters", "console.components.payload-formatters-form.index.learnMoreAboutCayenne": "What is CayenneLPP?", "console.components.payload-formatters-form.index.noRepositoryWarning": "The application formatter is set to `Repository` but this device does not have an associated formatter in the LoRaWAN Device repository. Messages for this end device will hence not be formatted.", + "console.components.payload-formatters-form.index.confirmNavigationTitle": "Confirm navigation", + "console.components.payload-formatters-form.index.confirmNavigationMessage": "Are you sure you want to leave this page? Your current changes have not been saved yet.", "console.components.payload-formatters-form.test-form.index.validResult": "Payload is valid", "console.components.payload-formatters-form.test-form.index.noResult": "No test result generated yet", "console.components.payload-formatters-form.test-form.index.testDecoder": "Test decoder", @@ -416,20 +418,6 @@ "console.components.uplink-form.uplink-form.simulateUplink": "Simulate uplink", "console.components.uplink-form.uplink-form.payloadDescription": "The desired payload bytes of the uplink message", "console.components.uplink-form.uplink-form.uplinkSuccess": "Uplink sent", - "console.components.user-data-form.index.adminLabel": "Grant this user admin status", - "console.components.user-data-form.index.adminDescription": "Admin status enables overarching rights such as managing other users or modifying entities regardless of collaboration status", - "console.components.user-data-form.index.userDescPlaceholder": "Description for my new user", - "console.components.user-data-form.index.userDescDescription": "Optional user description; can also be used to save notes about the user", - "console.components.user-data-form.index.userIdPlaceholder": "jane-doe", - "console.components.user-data-form.index.userNamePlaceholder": "Jane Doe", - "console.components.user-data-form.index.emailPlaceholder": "mail@example.com", - "console.components.user-data-form.index.emailAddressDescription": "Primary email address used for logging in; this address is not publicly visible", - "console.components.user-data-form.index.emailAddressValidation": "Treat email address as validated", - "console.components.user-data-form.index.emailAddressValidationDescription": "Enable this option if you do not need this user to validate the email address", - "console.components.user-data-form.index.deleteTitle": "Are you sure you want to delete this account?", - "console.components.user-data-form.index.deleteWarning": "This will PERMANENTLY DELETE THIS ACCOUNT and LOCK THE USER ID AND EMAIL FOR RE-REGISTRATION. Associated entities (e.g. gateways, applications and end devices) owned by this user that do not have any other collaborators will become UNACCESSIBLE and it will NOT BE POSSIBLE TO REGISTER ENTITIES WITH THE SAME ID OR EUI's AGAIN. Make sure you assign new collaborators to such entities if you plan to continue using them.", - "console.components.user-data-form.index.purgeWarning": "This will PERMANENTLY DELETE THIS ACCOUNT. Associated entities (e.g. gateways, applications and end devices) owned by this user that do not have any other collaborators will become UNACCESSIBLE and it will NOT BE POSSIBLE TO REGISTER ENTITIES WITH THE SAME ID OR EUI's AGAIN. Make sure you assign new collaborators to such entities if you plan to continue using them.", - "console.components.user-data-form.index.deleteConfirmMessage": "Please type in this user's user ID to confirm.", "console.components.webhook-form.index.idPlaceholder": "my-new-webhook", "console.components.webhook-form.index.messageInfo": "For each enabled event type an optional path can be defined which will be appended to the base URL", "console.components.webhook-form.index.deleteWebhook": "Delete Webhook", @@ -478,8 +466,6 @@ "console.containers.application-payload-formatters.uplink.title": "Default uplink payload formatter", "console.containers.application-payload-formatters.uplink.infoText": "You can use the \"Payload formatter\" tab of individual end devices to test uplink payload formatters and to define individual payload formatter settings per end device.", "console.containers.application-payload-formatters.uplink.uplinkResetWarning": "You do not have sufficient rights to view the current uplink payload formatter. Only overwriting is allowed.", - "console.containers.application-title-section.application-title-section.lastSeenAvailableTooltip": "The elapsed time since the network registered activity (sent uplinks, confirmed downlinks or (re)join requests) of the end device(s) in this application.", - "console.containers.application-title-section.application-title-section.noActivityTooltip": "The network has not recently registered any activity (sent uplinks, confirmed downlinks or (re)join requests) of the end device(s) in this application.", "console.containers.application-title-section.index.lastSeenAvailableTooltip": "The elapsed time since the network registered activity (sent uplinks, confirmed downlinks or (re)join requests) of the end device(s) in this application.", "console.containers.application-title-section.index.noActivityTooltip": "The network has not recently registered any activity (sent uplinks, confirmed downlinks or (re)join requests) of the end device(s) in this application.", "console.containers.applications-form.index.applicationName": "Application name", @@ -501,31 +487,31 @@ "console.containers.deployment-component-status.index.statusPage": "Go to status page", "console.containers.deployment-component-status.index.seeChangelog": "See changelog", "console.containers.dev-addr-input.index.devAddrFetchingFailure": "There was an error and the end device address could not be generated", - "console.containers.device-importer.index.proceed": "Proceed to end device list", - "console.containers.device-importer.index.retry": "Retry from scratch", - "console.containers.device-importer.index.abort": "Abort", - "console.containers.device-importer.index.converting": "Converting templates…", - "console.containers.device-importer.index.creating": "Creating end devices…", - "console.containers.device-importer.index.operationInProgress": "Operation in progress", - "console.containers.device-importer.index.operationHalted": "Operation halted", - "console.containers.device-importer.index.operationFinished": "Operation finished", - "console.containers.device-importer.index.operationAborted": "Operation aborted", - "console.containers.device-importer.index.errorTitle": "There was an error and the operation could not be completed", - "console.containers.device-importer.index.conversionErrorTitle": "Could not import devices", - "console.containers.device-importer.index.conversionErrorMessage": "An error occurred while processing the provided end device template. This could be due to invalid format, syntax or file encoding. Please check the provided template file and try again. See also our documentation on Importing End Devices for more information.", - "console.containers.device-importer.index.incompleteWarningTitle": "Not all devices imported successfully", - "console.containers.device-importer.index.incompleteWarningMessage": "{count, number} {count, plural, one {end device} other {end devices}} could not be imported successfully, because {count, plural, one {its} other {their}} registration attempt resulted in an error", - "console.containers.device-importer.index.incompleteStatus": "The registration of the following {count, plural, one {end device} other {end devices}} failed:", - "console.containers.device-importer.index.noneWarningTitle": "No end device was created", - "console.containers.device-importer.index.noneWarningMessage": "None of your specified end devices was imported, because each registration attempt resulted in an error", - "console.containers.device-importer.index.processLog": "Process log", - "console.containers.device-importer.index.progress": "Successfully converted {errorCount, number} of {deviceCount, number} {deviceCount, plural, one {end device} other {end devices}}", - "console.containers.device-importer.index.successInfoTitle": "All end devices imported successfully", - "console.containers.device-importer.index.successInfoMessage": "All of the specified end devices have been converted and imported successfully", - "console.containers.device-importer.index.documentationHint": "Please also see our documentation on Importing End Devices for more information and possible resolutions.", - "console.containers.device-importer.index.abortWarningTitle": "Device import aborted", - "console.containers.device-importer.index.abortWarningMessage": "The end device import was aborted and the remaining {count, number} {count, plural, one {end device} other {end devices}} have not been imported", - "console.containers.device-importer.index.largeFileWarningMessage": "Providing files larger than {warningThreshold} can cause issues during the import process. We recommend you to split such files up into multiple smaller files and importing them one by one.", + "console.containers.device-importer.messages.proceed": "Proceed to end device list", + "console.containers.device-importer.messages.retry": "Retry from scratch", + "console.containers.device-importer.messages.abort": "Abort", + "console.containers.device-importer.messages.converting": "Converting templates…", + "console.containers.device-importer.messages.creating": "Creating end devices…", + "console.containers.device-importer.messages.operationInProgress": "Operation in progress", + "console.containers.device-importer.messages.operationHalted": "Operation halted", + "console.containers.device-importer.messages.operationFinished": "Operation finished", + "console.containers.device-importer.messages.operationAborted": "Operation aborted", + "console.containers.device-importer.messages.errorTitle": "There was an error and the operation could not be completed", + "console.containers.device-importer.messages.conversionErrorTitle": "Could not import devices", + "console.containers.device-importer.messages.conversionErrorMessage": "An error occurred while processing the provided end device template. This could be due to invalid format, syntax or file encoding. Please check the provided template file and try again. See also our documentation on Importing End Devices for more information.", + "console.containers.device-importer.messages.incompleteWarningTitle": "Not all devices imported successfully", + "console.containers.device-importer.messages.incompleteWarningMessage": "{count, number} {count, plural, one {end device} other {end devices}} could not be imported successfully, because {count, plural, one {its} other {their}} registration attempt resulted in an error", + "console.containers.device-importer.messages.incompleteStatus": "The registration of the following {count, plural, one {end device} other {end devices}} failed:", + "console.containers.device-importer.messages.noneWarningTitle": "No end device was created", + "console.containers.device-importer.messages.noneWarningMessage": "None of your specified end devices was imported, because each registration attempt resulted in an error", + "console.containers.device-importer.messages.processLog": "Process log", + "console.containers.device-importer.messages.progress": "Successfully converted {errorCount, number} of {deviceCount, number} {deviceCount, plural, one {end device} other {end devices}}", + "console.containers.device-importer.messages.successInfoTitle": "All end devices imported successfully", + "console.containers.device-importer.messages.successInfoMessage": "All of the specified end devices have been converted and imported successfully", + "console.containers.device-importer.messages.documentationHint": "Please also see our documentation on Importing End Devices for more information and possible resolutions.", + "console.containers.device-importer.messages.abortWarningTitle": "Device import aborted", + "console.containers.device-importer.messages.abortWarningMessage": "The end device import was aborted and the remaining {count, number} {count, plural, one {end device} other {end devices}} have not been imported", + "console.containers.device-importer.messages.largeFileWarningMessage": "Providing files larger than {warningThreshold} can cause issues during the import process. We recommend you to split such files up into multiple smaller files and importing them one by one.", "console.containers.device-onboarding-form.messages.endDeviceType": "End device type", "console.containers.device-onboarding-form.messages.provisioningTitle": "Provisioning information", "console.containers.device-onboarding-form.messages.inputMethod": "Input Method", @@ -660,8 +646,6 @@ "console.containers.lora-cloud-gls-form.index.multiFrameTimeWindow": "Multiframe time window", "console.containers.lora-cloud-gls-form.index.multiFrameTimeWindowDescription": "The maximum age of considered historical messages in minutes", "console.containers.lora-cloud-gls-form.index.enableMultiFrame": "Enable multiframe", - "console.containers.move-away-modal.move-away-modal.modalTitle": "Confirm navigation", - "console.containers.move-away-modal.move-away-modal.modalMessage": "Are you sure you want to leave this page? Your current changes have not been saved yet.", "console.containers.network-information-container.index.openSourceInfo": "You are currently using The Things Stack Open Source. More features can be unlocked by using The Things Stack Cloud.", "console.containers.network-information-container.index.plansButton": "Get started with The things Stack Cloud", "console.containers.network-information-container.registry-totals.applicationsUsed": "Total applications", @@ -691,6 +675,32 @@ "console.containers.pubsub-formats-select.index.warning": "Pub/Sub formats unavailable", "console.containers.pubsubs-table.index.format": "Format", "console.containers.pubsubs-table.index.host": "Server host", + "console.containers.user-data-form.add.adminLabel": "Grant this user admin status", + "console.containers.user-data-form.add.adminDescription": "Admin status enables overarching rights such as managing other users or modifying entities regardless of collaboration status", + "console.containers.user-data-form.add.userDescPlaceholder": "Description for my new user", + "console.containers.user-data-form.add.userDescDescription": "Optional user description; can also be used to save notes about the user", + "console.containers.user-data-form.add.userIdPlaceholder": "jane-doe", + "console.containers.user-data-form.add.userNamePlaceholder": "Jane Doe", + "console.containers.user-data-form.add.emailPlaceholder": "mail@example.com", + "console.containers.user-data-form.add.emailAddressDescription": "Primary email address used for logging in; this address is not publicly visible", + "console.containers.user-data-form.add.emailAddressValidation": "Treat email address as validated", + "console.containers.user-data-form.add.emailAddressValidationDescription": "Enable this option if you do not need this user to validate the email address", + "console.containers.user-data-form.edit.adminLabel": "Grant this user admin status", + "console.containers.user-data-form.edit.adminDescription": "Admin status enables overarching rights such as managing other users or modifying entities regardless of collaboration status", + "console.containers.user-data-form.edit.userDescPlaceholder": "Description for my new user", + "console.containers.user-data-form.edit.userDescDescription": "Optional user description; can also be used to save notes about the user", + "console.containers.user-data-form.edit.userIdPlaceholder": "jane-doe", + "console.containers.user-data-form.edit.userNamePlaceholder": "Jane Doe", + "console.containers.user-data-form.edit.emailPlaceholder": "mail@example.com", + "console.containers.user-data-form.edit.emailAddressDescription": "Primary email address used for logging in; this address is not publicly visible", + "console.containers.user-data-form.edit.emailAddressValidation": "Treat email address as validated", + "console.containers.user-data-form.edit.emailAddressValidationDescription": "Enable this option if you do not need this user to validate the email address", + "console.containers.user-data-form.edit.deleteTitle": "Are you sure you want to delete this account?", + "console.containers.user-data-form.edit.deleteWarning": "This will PERMANENTLY DELETE THIS ACCOUNT and LOCK THE USER ID AND EMAIL FOR RE-REGISTRATION. Associated entities (e.g. gateways, applications and end devices) owned by this user that do not have any other collaborators will become UNACCESSIBLE and it will NOT BE POSSIBLE TO REGISTER ENTITIES WITH THE SAME ID OR EUI's AGAIN. Make sure you assign new collaborators to such entities if you plan to continue using them.", + "console.containers.user-data-form.edit.purgeWarning": "This will PERMANENTLY DELETE THIS ACCOUNT. Associated entities (e.g. gateways, applications and end devices) owned by this user that do not have any other collaborators will become UNACCESSIBLE and it will NOT BE POSSIBLE TO REGISTER ENTITIES WITH THE SAME ID OR EUI's AGAIN. Make sure you assign new collaborators to such entities if you plan to continue using them.", + "console.containers.user-data-form.edit.deleteConfirmMessage": "Please type in this user's user ID to confirm.", + "console.containers.user-data-form.edit.updateSuccess": "User updated", + "console.containers.user-data-form.edit.deleteSuccess": "User deleted", "console.containers.users-table.index.invite": "Invite user", "console.containers.users-table.index.revokeInvitation": "Revoke this invitation", "console.containers.users-table.index.sentAt": "Sent", @@ -828,8 +838,6 @@ "console.views.admin-packet-broker.messages.defaultGatewayVisibilitySet": "Default gateway visibility set", "console.views.admin-packet-broker.messages.packetBrokerStatusPage": "Packet Broker Status page", "console.views.admin-panel-network-information.index.title": "Network information", - "console.views.admin-user-management-edit.index.updateSuccess": "User updated", - "console.views.admin-user-management-edit.index.deleteSuccess": "User deleted", "console.views.application-add.index.appDescription": "Within applications, you can register and manage end devices and their network data. After setting up your device fleet, use one of our many integration options to pass relevant data to your external services.{break}Learn more in our guide on Adding Applications.", "console.views.application-data.index.appData": "Application data", "console.views.application-integrations-lora-cloud.index.loraCloudInfoText": "Lora Cloud provides value added APIs that enable simple solutions for common tasks related to LoRaWAN networks and LoRa-based devices. You can setup our LoRaCloud integrations below.",