Merge branch 'master' into MM-50966-in-product-expansion
@@ -1,4 +1,3 @@
|
||||
e2e/results
|
||||
node_modules
|
||||
dist
|
||||
lib
|
||||
|
||||
@@ -2,22 +2,19 @@
|
||||
"root": true,
|
||||
"extends": [
|
||||
"plugin:mattermost/react",
|
||||
"plugin:cypress/recommended",
|
||||
"plugin:react-hooks/recommended"
|
||||
],
|
||||
"plugins": [
|
||||
"@babel/eslint-plugin",
|
||||
"mattermost",
|
||||
"import",
|
||||
"cypress",
|
||||
"no-only-tests",
|
||||
"@typescript-eslint",
|
||||
"formatjs"
|
||||
],
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"env": {
|
||||
"jest": true,
|
||||
"cypress/globals": true
|
||||
"jest": true
|
||||
},
|
||||
"settings": {
|
||||
"import/resolver": "webpack",
|
||||
@@ -166,17 +163,6 @@
|
||||
"no-process-env": 0,
|
||||
"prefer-arrow-callback": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"files": ["e2e/**"],
|
||||
"rules": {
|
||||
"@babel/no-unused-expressions": 0,
|
||||
"func-names": 0,
|
||||
"import/no-unresolved": 0,
|
||||
"max-nested-callbacks": 0,
|
||||
"no-process-env": 0,
|
||||
"no-unused-expressions": 0
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
"properties": {
|
||||
"GomodMainOnly": true,
|
||||
"GomodExcludes": "_tests/|[-_]generators?$|^plugin/checker$",
|
||||
"NpmExcludes": "/e2e$",
|
||||
"GradlePath": "./gradlew:../gradlew"
|
||||
},
|
||||
"filters": [
|
||||
|
||||
@@ -6,58 +6,6 @@ export NODE_OPTIONS=--max-old-space-size=4096
|
||||
i18n-extract: ## Extract strings for translation from the source code
|
||||
npm run i18n-extract
|
||||
|
||||
e2e/playwright/node_modules:
|
||||
@echo Install Playwright and its dependencies
|
||||
cd e2e/playwright && npm install
|
||||
|
||||
.PHONY: e2e-test
|
||||
e2e-test:
|
||||
@echo E2E: Running mattermost-mysql-e2e
|
||||
@if [ $(shell docker ps -a | grep -ci mattermost-mysql-e2e) -eq 0 ]; then \
|
||||
echo starting mattermost-mysql-e2e; \
|
||||
docker run --name mattermost-mysql-e2e -p 35476:3306 -e MYSQL_ROOT_PASSWORD=mostest \
|
||||
-e MYSQL_USER=mmuser -e MYSQL_PASSWORD=mostest -e MYSQL_DATABASE=mattermost_test -d mysql:5.7 > /dev/null; \
|
||||
elif [ $(shell docker ps | grep -ci mattermost-mysql-e2e) -eq 0 ]; then \
|
||||
echo restarting mattermost-mysql-e2e; \
|
||||
docker start mattermost-mysql-e2e > /dev/null; \
|
||||
fi
|
||||
|
||||
cd $(BUILD_SERVER_DIR) && [[ -f config/config.json ]] && \
|
||||
cp config/config.json config/config-backup.json && make config-reset || \
|
||||
echo "config.json not found" && make config-reset
|
||||
|
||||
@echo E2E: Starting the server
|
||||
cd $(BUILD_SERVER_DIR) && $(MAKE) run
|
||||
|
||||
@echo E2E: Generating test data
|
||||
cd $(BUILD_SERVER_DIR) && $(MAKE) test-data
|
||||
|
||||
@echo E2E: Running end-to-end testing
|
||||
cd e2e && npm install && npm run cypress:run
|
||||
|
||||
@echo E2E: Stoppping the server
|
||||
cd $(BUILD_SERVER_DIR) && $(MAKE) stop
|
||||
|
||||
@echo E2E: stopping mattermost-mysql-e2e
|
||||
docker stop mattermost-mysql-e2e > /dev/null
|
||||
|
||||
cd $(BUILD_SERVER_DIR) && [[ -f config/config-backup.json ]] && \
|
||||
cp config/config-backup.json config/config.json && echo "revert local config.json" || \
|
||||
echo "config-backup.json not found" && sed -i'' -e 's|"DataSource": ".*"|"DataSource": "mmuser:mostest@tcp(dockerhost:3306)/mattermost_test?charset=utf8mb4,utf8\u0026readTimeout=30s\u0026writeTimeout=30s"|g' config/config.json
|
||||
|
||||
@echo E2E: Tests completed
|
||||
|
||||
.PHONY: clean-e2e
|
||||
clean-e2e:
|
||||
@if [ $(shell docker ps -a | grep -ci mattermost-mysql-e2e) -eq 1 ]; then \
|
||||
echo stopping mattermost-mysql-e2e; \
|
||||
docker stop mattermost-mysql-e2e > /dev/null; \
|
||||
fi
|
||||
|
||||
cd $(BUILD_SERVER_DIR) && [[ -f config/config-backup.json ]] && \
|
||||
cp config/config-backup.json config/config.json && echo "revert local config.json" || \
|
||||
echo "config-backup.json not found" && sed -i'' -e 's|"DataSource": ".*"|"DataSource": "mmuser:mostest@tcp(dockerhost:3306)/mattermost_test?charset=utf8mb4,utf8\u0026readTimeout=30s\u0026writeTimeout=30s"|g' config/config.json
|
||||
|
||||
.PHONY: emojis
|
||||
emojis: ## Creates emoji JSON, JSX and Go files and extracts emoji images from the system font
|
||||
SERVER_DIR=$(BUILD_SERVER_DIR) npm run make-emojis
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
const config = {
|
||||
snapshotSerializers: ['enzyme-to-json/serializer'],
|
||||
testPathIgnorePatterns: ['/node_modules/', '/e2e/'],
|
||||
testPathIgnorePatterns: ['/node_modules/'],
|
||||
clearMocks: true,
|
||||
collectCoverageFrom: [
|
||||
'actions/src/**/*.{js,jsx,ts,tsx}',
|
||||
@@ -17,7 +17,6 @@ const config = {
|
||||
'selectors/src/**/*.{js,jsx,ts,tsx}',
|
||||
'stores/src/**/*.{js,jsx,ts,tsx}',
|
||||
'utils/src/**/*.{js,jsx,ts,tsx}',
|
||||
'!e2e/**',
|
||||
],
|
||||
coverageReporters: ['lcov', 'text-summary'],
|
||||
moduleNameMapper: {
|
||||
|
||||
@@ -169,7 +169,6 @@
|
||||
"enzyme-to-json": "3.6.2",
|
||||
"eslint": "7.32.0",
|
||||
"eslint-import-resolver-webpack": "0.13.2",
|
||||
"eslint-plugin-cypress": "2.11.3",
|
||||
"eslint-plugin-formatjs": "4.3.4",
|
||||
"eslint-plugin-header": "3.1.1",
|
||||
"eslint-plugin-import": "2.23.4",
|
||||
|
||||
@@ -35,7 +35,7 @@ import KeyboardShortcutsModal from 'components/keyboard_shortcuts/keyboard_short
|
||||
import {GlobalState} from 'types/store';
|
||||
|
||||
import {t} from 'utils/i18n';
|
||||
import MarketplaceModal from 'components/plugin_marketplace';
|
||||
import MarketplaceModal from 'components/plugin_marketplace/marketplace_modal';
|
||||
import WorkTemplateModal from 'components/work_templates';
|
||||
import {haveICurrentTeamPermission} from 'mattermost-redux/selectors/entities/roles';
|
||||
import {Permissions} from 'mattermost-redux/constants';
|
||||
|
||||
@@ -59,7 +59,7 @@ exports[`components/GenericModal should match snapshot for base case 1`] = `
|
||||
</h1>
|
||||
</div>
|
||||
<div
|
||||
className="GenericModal__body"
|
||||
className="GenericModal__body padding"
|
||||
/>
|
||||
</ModalBody>
|
||||
</div>
|
||||
@@ -126,11 +126,12 @@ exports[`components/GenericModal should match snapshot with both buttons 1`] = `
|
||||
</h1>
|
||||
</div>
|
||||
<div
|
||||
className="GenericModal__body"
|
||||
className="GenericModal__body padding"
|
||||
/>
|
||||
</ModalBody>
|
||||
<ModalFooter
|
||||
bsClass="modal-footer"
|
||||
className=""
|
||||
componentClass="div"
|
||||
>
|
||||
<button
|
||||
@@ -218,11 +219,12 @@ exports[`components/GenericModal should match snapshot with disabled confirm but
|
||||
</h1>
|
||||
</div>
|
||||
<div
|
||||
className="GenericModal__body"
|
||||
className="GenericModal__body padding"
|
||||
/>
|
||||
</ModalBody>
|
||||
<ModalFooter
|
||||
bsClass="modal-footer"
|
||||
className=""
|
||||
componentClass="div"
|
||||
>
|
||||
<button
|
||||
|
||||
@@ -19,8 +19,7 @@ import {Locations, Constants, ModalIdentifiers} from 'utils/constants';
|
||||
import Permissions from 'mattermost-redux/constants/permissions';
|
||||
import {ActionsTutorialTip} from 'components/actions_menu/actions_menu_tutorial_tip';
|
||||
import {ModalData} from 'types/actions';
|
||||
import MarketplaceModal from 'components/plugin_marketplace';
|
||||
import {OpenedFromType} from 'components/plugin_marketplace/marketplace_modal';
|
||||
import MarketplaceModal, {OpenedFromType} from 'components/plugin_marketplace/marketplace_modal';
|
||||
import OverlayTrigger from 'components/overlay_trigger';
|
||||
import * as PostUtils from 'utils/post_utils';
|
||||
import * as Utils from 'utils/utils';
|
||||
|
||||
@@ -10,7 +10,7 @@ import Icon from '@mattermost/compass-components/foundations/icon';
|
||||
|
||||
import {openModal} from 'actions/views/modals';
|
||||
|
||||
import MarketplaceModal from 'components/plugin_marketplace';
|
||||
import MarketplaceModal from 'components/plugin_marketplace/marketplace_modal';
|
||||
import OverlayTrigger from 'components/overlay_trigger';
|
||||
|
||||
import {Constants, ModalIdentifiers} from 'utils/constants';
|
||||
|
||||
@@ -10,7 +10,7 @@ import {uniqWith} from 'lodash';
|
||||
import {Post} from '@mattermost/types/posts';
|
||||
import {MarketplacePlugin} from '@mattermost/types/marketplace';
|
||||
|
||||
import MarketplaceModal from 'components/plugin_marketplace';
|
||||
import MarketplaceModal from 'components/plugin_marketplace/marketplace_modal';
|
||||
import Markdown from 'components/markdown';
|
||||
|
||||
import {getUsers} from 'mattermost-redux/selectors/entities/users';
|
||||
|
||||
@@ -4,6 +4,7 @@ exports[`components/drafts/draft_actions/delete_draft_modal should have called o
|
||||
<GenericModal
|
||||
autoCloseOnCancelButton={true}
|
||||
autoCloseOnConfirmButton={true}
|
||||
bodyPadding={true}
|
||||
compassDesign={true}
|
||||
confirmButtonText="Yes, delete"
|
||||
enforceFocus={true}
|
||||
@@ -45,6 +46,7 @@ exports[`components/drafts/draft_actions/delete_draft_modal should have called o
|
||||
<GenericModal
|
||||
autoCloseOnCancelButton={true}
|
||||
autoCloseOnConfirmButton={true}
|
||||
bodyPadding={true}
|
||||
compassDesign={true}
|
||||
confirmButtonText="Yes, delete"
|
||||
enforceFocus={true}
|
||||
|
||||
@@ -4,6 +4,7 @@ exports[`components/drafts/draft_actions/send_draft_modal should have called onC
|
||||
<GenericModal
|
||||
autoCloseOnCancelButton={true}
|
||||
autoCloseOnConfirmButton={true}
|
||||
bodyPadding={true}
|
||||
compassDesign={true}
|
||||
confirmButtonText="Yes, send now"
|
||||
enforceFocus={true}
|
||||
@@ -44,6 +45,7 @@ exports[`components/drafts/draft_actions/send_draft_modal should have called onE
|
||||
<GenericModal
|
||||
autoCloseOnCancelButton={true}
|
||||
autoCloseOnConfirmButton={true}
|
||||
bodyPadding={true}
|
||||
compassDesign={true}
|
||||
confirmButtonText="Yes, send now"
|
||||
enforceFocus={true}
|
||||
|
||||
@@ -104,6 +104,10 @@
|
||||
padding: 12px 24px 24px 24px;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
|
||||
&.divider {
|
||||
border-top: 1px solid rgba(63, 67, 80, 0.08);
|
||||
}
|
||||
}
|
||||
|
||||
&.GenericModal__compassDesign {
|
||||
@@ -112,10 +116,11 @@
|
||||
border-radius: 12px;
|
||||
|
||||
.modal-header {
|
||||
flex-direction: column;
|
||||
padding: 26px 32px 7px;
|
||||
|
||||
.close {
|
||||
top: 20px;
|
||||
top: 22px;
|
||||
right: 18px;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
@@ -133,7 +138,13 @@
|
||||
}
|
||||
|
||||
.GenericModal__header {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 0;
|
||||
margin-right: 32px;
|
||||
border-top-left-radius: 12px;
|
||||
border-top-right-radius: 12px;
|
||||
|
||||
@@ -145,10 +156,12 @@
|
||||
}
|
||||
|
||||
.modal-body {
|
||||
padding: 15px 32px 0;
|
||||
|
||||
.GenericModal__body {
|
||||
padding: 0;
|
||||
|
||||
&.padding {
|
||||
padding: 15px 32px 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -11,5 +11,8 @@ import './generic_modal.scss';
|
||||
// find them there, so we've had to redefine them here
|
||||
t('generic_modal.cancel');
|
||||
t('generic_modal.confirm');
|
||||
t('footer_pagination.count');
|
||||
t('footer_pagination.prev');
|
||||
t('footer_pagination.next');
|
||||
|
||||
export default GenericModal;
|
||||
|
||||
@@ -152,14 +152,7 @@ exports[`components/global/product_switcher_menu should match snapshot with id 1
|
||||
"openedFrom": "product_menu",
|
||||
}
|
||||
}
|
||||
dialogType={
|
||||
Object {
|
||||
"$$typeof": Symbol(react.memo),
|
||||
"WrappedComponent": [Function],
|
||||
"compare": null,
|
||||
"type": [Function],
|
||||
}
|
||||
}
|
||||
dialogType={[Function]}
|
||||
icon={
|
||||
<Icon
|
||||
glyph="view-grid-plus-outline"
|
||||
@@ -360,14 +353,7 @@ exports[`components/global/product_switcher_menu should match snapshot with most
|
||||
"openedFrom": "product_menu",
|
||||
}
|
||||
}
|
||||
dialogType={
|
||||
Object {
|
||||
"$$typeof": Symbol(react.memo),
|
||||
"WrappedComponent": [Function],
|
||||
"compare": null,
|
||||
"type": [Function],
|
||||
}
|
||||
}
|
||||
dialogType={[Function]}
|
||||
icon={
|
||||
<Icon
|
||||
glyph="view-grid-plus-outline"
|
||||
@@ -688,14 +674,7 @@ exports[`components/global/product_switcher_menu should show integrations should
|
||||
"openedFrom": "product_menu",
|
||||
}
|
||||
}
|
||||
dialogType={
|
||||
Object {
|
||||
"$$typeof": Symbol(react.memo),
|
||||
"WrappedComponent": [Function],
|
||||
"compare": null,
|
||||
"type": [Function],
|
||||
}
|
||||
}
|
||||
dialogType={[Function]}
|
||||
icon={
|
||||
<Icon
|
||||
glyph="view-grid-plus-outline"
|
||||
|
||||
@@ -11,7 +11,7 @@ import {Permissions} from 'mattermost-redux/constants';
|
||||
import AboutBuildModal from 'components/about_build_modal';
|
||||
import SystemPermissionGate from 'components/permissions_gates/system_permission_gate';
|
||||
import TeamPermissionGate from 'components/permissions_gates/team_permission_gate';
|
||||
import MarketplaceModal from 'components/plugin_marketplace';
|
||||
import MarketplaceModal from 'components/plugin_marketplace/marketplace_modal';
|
||||
import Menu from 'components/widgets/menu/menu';
|
||||
import RestrictedIndicator from 'components/widgets/menu/menu_items/restricted_indicator';
|
||||
import {VisitSystemConsoleTour} from 'components/onboarding_tasks';
|
||||
|
||||
@@ -249,6 +249,7 @@ export default class AddBot extends React.PureComponent<Props, State> {
|
||||
/>
|
||||
),
|
||||
});
|
||||
return;
|
||||
} else if (this.state.pictureFile.size > this.props.maxFileSize) {
|
||||
this.setState({
|
||||
error: (
|
||||
@@ -258,6 +259,7 @@ export default class AddBot extends React.PureComponent<Props, State> {
|
||||
/>
|
||||
),
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -51,6 +51,10 @@ button.InviteView__copyLink.tertiary-button:focus {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&-guest {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.btn {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
import React, {useEffect, useMemo} from 'react';
|
||||
import {Modal} from 'react-bootstrap';
|
||||
import {FormattedMessage, useIntl} from 'react-intl';
|
||||
import classNames from 'classnames';
|
||||
|
||||
import deepFreeze from 'mattermost-redux/utils/deep_freeze';
|
||||
import {Channel} from '@mattermost/types/channels';
|
||||
@@ -101,6 +102,7 @@ export default function InviteView(props: Props) {
|
||||
}, {inviteURL})
|
||||
}
|
||||
className='InviteView__copyLink'
|
||||
aria-live='polite'
|
||||
>
|
||||
{!copyText.copiedRecently && (
|
||||
<>
|
||||
@@ -259,8 +261,8 @@ export default function InviteView(props: Props) {
|
||||
)}
|
||||
<OverageUsersBannerNotice/>
|
||||
</Modal.Body>
|
||||
<Modal.Footer className={'InviteView__footer ' + props.footerClass}>
|
||||
{copyButton}
|
||||
<Modal.Footer className={classNames('InviteView__footer', props.footerClass, {'InviteView__footer-guest': props.inviteType === InviteType.GUEST})}>
|
||||
{props.inviteType === InviteType.MEMBER && copyButton}
|
||||
<button
|
||||
disabled={!isInviteValid}
|
||||
onClick={props.invite}
|
||||
|
||||
@@ -4,6 +4,7 @@ exports[`components/new_channel_modal should match snapshot 1`] = `
|
||||
<GenericModal
|
||||
autoCloseOnCancelButton={true}
|
||||
autoCloseOnConfirmButton={false}
|
||||
bodyPadding={true}
|
||||
cancelButtonText="Cancel"
|
||||
className="new-channel-modal"
|
||||
compassDesign={true}
|
||||
|
||||
@@ -51,7 +51,7 @@ exports[`components/onboarding_tasklist/onboarding_tasklist_completed.tsx should
|
||||
>
|
||||
<MemoizedFormattedMessage
|
||||
defaultMessage="No, thanks"
|
||||
id="onboardingTask.checklist.no_tanks"
|
||||
id="onboardingTask.checklist.no_thanks"
|
||||
/>
|
||||
</button>
|
||||
<div
|
||||
|
||||
@@ -216,7 +216,7 @@ const Completed = (props: Props): JSX.Element => {
|
||||
className={'no-thanks-link style-link'}
|
||||
>
|
||||
<FormattedMessage
|
||||
id={'onboardingTask.checklist.no_tanks'}
|
||||
id={'onboardingTask.checklist.no_thanks'}
|
||||
defaultMessage='No, thanks'
|
||||
/>
|
||||
</button>
|
||||
|
||||
@@ -65,7 +65,7 @@ const useGetTaskDetails = () => {
|
||||
svg: Newspaper,
|
||||
message: formatMessage({
|
||||
id: 'onboardingTask.checklist.task_create_from_work_template',
|
||||
defaultMessage: 'Create from a template - set up a channel with linked boards and playbooks.',
|
||||
defaultMessage: 'Create from a template',
|
||||
}),
|
||||
},
|
||||
[OnboardingTasksName.CHANNELS_TOUR]: {
|
||||
|
||||
@@ -25,4 +25,5 @@ function devConfirmCardSetup(confirmCardSetup: ConfirmCardSetupType): ConfirmCar
|
||||
export const getConfirmCardSetup = (isDevMode?: boolean) => (isDevMode ? devConfirmCardSetup : prodConfirmCardSetup);
|
||||
|
||||
export const STRIPE_CSS_SRC = 'https://fonts.googleapis.com/css?family=Open+Sans:400,400i,600,600i&display=swap';
|
||||
export const STRIPE_PUBLIC_KEY = 'pk_test_ttEpW6dCHksKyfAFzh6MvgBj';
|
||||
//eslint-disable-next-line no-process-env
|
||||
export const STRIPE_PUBLIC_KEY = process.env.STRIPE_PUBLIC_KEY || 'pk_test_ttEpW6dCHksKyfAFzh6MvgBj';
|
||||
|
||||
@@ -1,58 +0,0 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import React from 'react';
|
||||
import {connect} from 'react-redux';
|
||||
import {bindActionCreators, Dispatch, ActionCreatorsMapObject} from 'redux';
|
||||
|
||||
import {GenericAction, ActionFunc} from 'mattermost-redux/types/actions';
|
||||
|
||||
import {GlobalState} from 'types/store';
|
||||
import {getListing, getInstalledListing} from 'selectors/views/marketplace';
|
||||
import {setFirstAdminVisitMarketplaceStatus} from 'mattermost-redux/actions/general';
|
||||
import {getPluginStatuses} from 'mattermost-redux/actions/admin';
|
||||
import {getFirstAdminVisitMarketplaceStatus} from 'mattermost-redux/selectors/entities/general';
|
||||
|
||||
import {makeAsyncComponent} from 'components/async_load';
|
||||
|
||||
import {isModalOpen} from 'selectors/views/modals';
|
||||
import {ModalIdentifiers} from 'utils/constants';
|
||||
import {getSiteURL} from 'utils/url';
|
||||
|
||||
import {closeModal} from 'actions/views/modals';
|
||||
import {fetchListing, filterListing} from 'actions/marketplace';
|
||||
|
||||
const MarketplaceModal = makeAsyncComponent('MarketplaceModal', React.lazy(() => import('./marketplace_modal')));
|
||||
|
||||
function mapStateToProps(state: GlobalState) {
|
||||
return {
|
||||
show: isModalOpen(state, ModalIdentifiers.PLUGIN_MARKETPLACE),
|
||||
listing: getListing(state),
|
||||
installedListing: getInstalledListing(state),
|
||||
siteURL: getSiteURL(),
|
||||
pluginStatuses: state.entities.admin.pluginStatuses,
|
||||
firstAdminVisitMarketplaceStatus: getFirstAdminVisitMarketplaceStatus(state),
|
||||
};
|
||||
}
|
||||
|
||||
type Actions = {
|
||||
closeModal(): void;
|
||||
fetchListing(localOnly?: boolean): Promise<{error?: Error}>;
|
||||
filterListing(filter: string): Promise<{error?: Error}>;
|
||||
setFirstAdminVisitMarketplaceStatus(): Promise<void>;
|
||||
getPluginStatuses(): Promise<void>;
|
||||
}
|
||||
|
||||
function mapDispatchToProps(dispatch: Dispatch<GenericAction>) {
|
||||
return {
|
||||
actions: bindActionCreators<ActionCreatorsMapObject<ActionFunc | GenericAction>, Actions>({
|
||||
closeModal: () => closeModal(ModalIdentifiers.PLUGIN_MARKETPLACE),
|
||||
fetchListing,
|
||||
filterListing,
|
||||
setFirstAdminVisitMarketplaceStatus,
|
||||
getPluginStatuses,
|
||||
}, dispatch),
|
||||
};
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(MarketplaceModal);
|
||||
@@ -11,7 +11,7 @@ exports[`components/MarketplaceItemApp MarketplaceItem should render 1`] = `
|
||||
}
|
||||
button={
|
||||
<button
|
||||
className="btn btn-primary"
|
||||
className="app-install always-show-enabled"
|
||||
disabled={false}
|
||||
onClick={[Function]}
|
||||
>
|
||||
@@ -50,7 +50,7 @@ exports[`components/MarketplaceItemApp MarketplaceItem should render installed a
|
||||
}
|
||||
button={
|
||||
<button
|
||||
className="btn btn-outline"
|
||||
className="app-installed"
|
||||
disabled={true}
|
||||
>
|
||||
<Memo(MemoizedFormattedMessage)
|
||||
@@ -83,7 +83,7 @@ exports[`components/MarketplaceItemApp MarketplaceItem should render with empty
|
||||
}
|
||||
button={
|
||||
<button
|
||||
className="btn btn-primary"
|
||||
className="app-install always-show-enabled"
|
||||
disabled={false}
|
||||
onClick={[Function]}
|
||||
>
|
||||
@@ -123,7 +123,7 @@ exports[`components/MarketplaceItemApp MarketplaceItem should render with icon 1
|
||||
}
|
||||
button={
|
||||
<button
|
||||
className="btn btn-primary"
|
||||
className="app-install always-show-enabled"
|
||||
disabled={false}
|
||||
onClick={[Function]}
|
||||
>
|
||||
@@ -164,7 +164,7 @@ exports[`components/MarketplaceItemApp MarketplaceItem should render with no hom
|
||||
}
|
||||
button={
|
||||
<button
|
||||
className="btn btn-primary"
|
||||
className="app-install always-show-enabled"
|
||||
disabled={false}
|
||||
onClick={[Function]}
|
||||
>
|
||||
@@ -202,7 +202,7 @@ exports[`components/MarketplaceItemApp MarketplaceItem should render with no plu
|
||||
}
|
||||
button={
|
||||
<button
|
||||
className="btn btn-primary"
|
||||
className="app-install always-show-enabled"
|
||||
disabled={false}
|
||||
onClick={[Function]}
|
||||
>
|
||||
@@ -240,7 +240,7 @@ exports[`components/MarketplaceItemApp MarketplaceItem should render with one la
|
||||
}
|
||||
button={
|
||||
<button
|
||||
className="btn btn-primary"
|
||||
className="app-install always-show-enabled"
|
||||
disabled={false}
|
||||
onClick={[Function]}
|
||||
>
|
||||
@@ -288,7 +288,7 @@ exports[`components/MarketplaceItemApp MarketplaceItem should render with server
|
||||
}
|
||||
button={
|
||||
<button
|
||||
className="btn btn-primary"
|
||||
className="app-install always-show-enabled"
|
||||
disabled={false}
|
||||
onClick={[Function]}
|
||||
>
|
||||
@@ -328,7 +328,7 @@ exports[`components/MarketplaceItemApp MarketplaceItem should render with two la
|
||||
}
|
||||
button={
|
||||
<button
|
||||
className="btn btn-primary"
|
||||
className="app-install always-show-enabled"
|
||||
disabled={false}
|
||||
onClick={[Function]}
|
||||
>
|
||||
@@ -381,7 +381,7 @@ exports[`components/MarketplaceItemApp MarketplaceItem when installing 1`] = `
|
||||
}
|
||||
button={
|
||||
<button
|
||||
className="btn btn-primary"
|
||||
className="app-install always-show-enabled"
|
||||
disabled={false}
|
||||
onClick={[Function]}
|
||||
>
|
||||
|
||||
@@ -50,7 +50,7 @@ export default class MarketplaceItemApp extends React.PureComponent <Marketplace
|
||||
if (this.props.installed && !this.props.installing && !this.props.error) {
|
||||
return (
|
||||
<button
|
||||
className='btn btn-outline'
|
||||
className='app-installed'
|
||||
disabled={true}
|
||||
>
|
||||
<FormattedMessage
|
||||
@@ -81,7 +81,7 @@ export default class MarketplaceItemApp extends React.PureComponent <Marketplace
|
||||
return (
|
||||
<button
|
||||
onClick={this.onInstall}
|
||||
className='btn btn-primary'
|
||||
className='app-install always-show-enabled'
|
||||
disabled={this.props.installing}
|
||||
>
|
||||
<LoadingWrapper
|
||||
|
||||
@@ -11,7 +11,7 @@ exports[`components/MarketplaceItemPlugin MarketplaceItem should render 1`] = `
|
||||
}
|
||||
button={
|
||||
<button
|
||||
className="btn btn-primary"
|
||||
className="plugin-install always-show-enabled"
|
||||
disabled={false}
|
||||
onClick={[Function]}
|
||||
>
|
||||
@@ -78,7 +78,7 @@ exports[`components/MarketplaceItemPlugin MarketplaceItem should render installe
|
||||
to="/admin_console/plugins/plugin_id"
|
||||
>
|
||||
<button
|
||||
className="btn btn-outline"
|
||||
className="plugin-configure"
|
||||
onClick={[Function]}
|
||||
>
|
||||
<Memo(MemoizedFormattedMessage)
|
||||
@@ -137,7 +137,7 @@ exports[`components/MarketplaceItemPlugin MarketplaceItem should render with emp
|
||||
}
|
||||
button={
|
||||
<button
|
||||
className="btn btn-primary"
|
||||
className="plugin-install always-show-enabled"
|
||||
disabled={false}
|
||||
onClick={[Function]}
|
||||
>
|
||||
@@ -202,7 +202,7 @@ exports[`components/MarketplaceItemPlugin MarketplaceItem should render with no
|
||||
}
|
||||
button={
|
||||
<button
|
||||
className="btn btn-primary"
|
||||
className="plugin-install always-show-enabled"
|
||||
disabled={false}
|
||||
onClick={[Function]}
|
||||
>
|
||||
@@ -265,7 +265,7 @@ exports[`components/MarketplaceItemPlugin MarketplaceItem should render with no
|
||||
}
|
||||
button={
|
||||
<button
|
||||
className="btn btn-primary"
|
||||
className="plugin-install always-show-enabled"
|
||||
disabled={false}
|
||||
onClick={[Function]}
|
||||
>
|
||||
@@ -328,7 +328,7 @@ exports[`components/MarketplaceItemPlugin MarketplaceItem should render with no
|
||||
}
|
||||
button={
|
||||
<button
|
||||
className="btn btn-primary"
|
||||
className="plugin-install always-show-enabled"
|
||||
disabled={false}
|
||||
onClick={[Function]}
|
||||
>
|
||||
@@ -390,7 +390,7 @@ exports[`components/MarketplaceItemPlugin MarketplaceItem should render with one
|
||||
}
|
||||
button={
|
||||
<button
|
||||
className="btn btn-primary"
|
||||
className="plugin-install always-show-enabled"
|
||||
disabled={false}
|
||||
onClick={[Function]}
|
||||
>
|
||||
@@ -463,7 +463,7 @@ exports[`components/MarketplaceItemPlugin MarketplaceItem should render with plu
|
||||
}
|
||||
button={
|
||||
<button
|
||||
className="btn btn-primary"
|
||||
className="plugin-install always-show-enabled"
|
||||
disabled={false}
|
||||
onClick={[Function]}
|
||||
>
|
||||
@@ -540,7 +540,7 @@ exports[`components/MarketplaceItemPlugin MarketplaceItem should render with ser
|
||||
}
|
||||
button={
|
||||
<button
|
||||
className="btn btn-primary"
|
||||
className="plugin-install always-show-enabled"
|
||||
disabled={false}
|
||||
onClick={[Function]}
|
||||
>
|
||||
@@ -605,7 +605,7 @@ exports[`components/MarketplaceItemPlugin MarketplaceItem should render with two
|
||||
}
|
||||
button={
|
||||
<button
|
||||
className="btn btn-primary"
|
||||
className="plugin-install always-show-enabled"
|
||||
disabled={false}
|
||||
onClick={[Function]}
|
||||
>
|
||||
@@ -686,7 +686,7 @@ exports[`components/MarketplaceItemPlugin MarketplaceItem should render with upd
|
||||
to="/admin_console/plugins/plugin_id"
|
||||
>
|
||||
<button
|
||||
className="btn btn-outline"
|
||||
className="plugin-configure"
|
||||
onClick={[Function]}
|
||||
>
|
||||
<Memo(MemoizedFormattedMessage)
|
||||
@@ -751,7 +751,7 @@ exports[`components/MarketplaceItemPlugin MarketplaceItem should render with upd
|
||||
to="/admin_console/plugins/plugin_id"
|
||||
>
|
||||
<button
|
||||
className="btn btn-outline"
|
||||
className="plugin-configure"
|
||||
onClick={[Function]}
|
||||
>
|
||||
<Memo(MemoizedFormattedMessage)
|
||||
|
||||
@@ -294,7 +294,7 @@ export default class MarketplaceItemPlugin extends React.PureComponent <Marketpl
|
||||
>
|
||||
<button
|
||||
onClick={this.onConfigure}
|
||||
className='btn btn-outline'
|
||||
className='plugin-configure'
|
||||
>
|
||||
<FormattedMessage
|
||||
id='marketplace_modal.list.configure'
|
||||
@@ -325,7 +325,7 @@ export default class MarketplaceItemPlugin extends React.PureComponent <Marketpl
|
||||
return (
|
||||
<button
|
||||
onClick={this.onInstall}
|
||||
className='btn btn-primary'
|
||||
className='plugin-install always-show-enabled'
|
||||
disabled={this.props.installing}
|
||||
>
|
||||
<LoadingWrapper
|
||||
|
||||
@@ -1,150 +1,14 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`components/marketplace/marketplace_list should render with multiple plugins 1`] = `
|
||||
exports[`components/marketplace/marketplace_list should render default 1`] = `
|
||||
<div
|
||||
className="more-modal__list"
|
||||
className="no_plugins"
|
||||
>
|
||||
<Connect(MarketplaceItemPlugin)
|
||||
description="This plugin sends quarterly user satisfaction surveys to gather feedback and help improve Mattermost"
|
||||
homepageUrl="https://github.com/mattermost/mattermost-plugin-nps"
|
||||
id="com.mattermost.nps"
|
||||
installedVersion=""
|
||||
key="com.mattermost.nps"
|
||||
name="User Satisfaction Surveys"
|
||||
version="1.0.3"
|
||||
<PluginIcon
|
||||
className="icon__plugin"
|
||||
/>
|
||||
<Connect(MarketplaceItemPlugin)
|
||||
description="This plugin sends quarterly user satisfaction surveys to gather feedback and help improve Mattermost"
|
||||
homepageUrl="https://github.com/mattermost/mattermost-plugin-nps"
|
||||
id="com.mattermost.nps"
|
||||
installedVersion=""
|
||||
key="com.mattermost.nps"
|
||||
name="User Satisfaction Surveys"
|
||||
version="1.0.3"
|
||||
/>
|
||||
<Connect(MarketplaceItemPlugin)
|
||||
description="This plugin sends quarterly user satisfaction surveys to gather feedback and help improve Mattermost"
|
||||
homepageUrl="https://github.com/mattermost/mattermost-plugin-nps"
|
||||
id="com.mattermost.nps"
|
||||
installedVersion=""
|
||||
key="com.mattermost.nps"
|
||||
name="User Satisfaction Surveys"
|
||||
version="1.0.3"
|
||||
/>
|
||||
<Connect(MarketplaceItemPlugin)
|
||||
description="This plugin sends quarterly user satisfaction surveys to gather feedback and help improve Mattermost"
|
||||
homepageUrl="https://github.com/mattermost/mattermost-plugin-nps"
|
||||
id="com.mattermost.nps"
|
||||
installedVersion=""
|
||||
key="com.mattermost.nps"
|
||||
name="User Satisfaction Surveys"
|
||||
version="1.0.3"
|
||||
/>
|
||||
<Connect(MarketplaceItemPlugin)
|
||||
description="This plugin sends quarterly user satisfaction surveys to gather feedback and help improve Mattermost"
|
||||
homepageUrl="https://github.com/mattermost/mattermost-plugin-nps"
|
||||
id="com.mattermost.nps"
|
||||
installedVersion=""
|
||||
key="com.mattermost.nps"
|
||||
name="User Satisfaction Surveys"
|
||||
version="1.0.3"
|
||||
/>
|
||||
<Connect(MarketplaceItemPlugin)
|
||||
description="This plugin sends quarterly user satisfaction surveys to gather feedback and help improve Mattermost"
|
||||
homepageUrl="https://github.com/mattermost/mattermost-plugin-nps"
|
||||
id="com.mattermost.nps"
|
||||
installedVersion=""
|
||||
key="com.mattermost.nps"
|
||||
name="User Satisfaction Surveys"
|
||||
version="1.0.3"
|
||||
/>
|
||||
<Connect(MarketplaceItemPlugin)
|
||||
description="This plugin sends quarterly user satisfaction surveys to gather feedback and help improve Mattermost"
|
||||
homepageUrl="https://github.com/mattermost/mattermost-plugin-nps"
|
||||
id="com.mattermost.nps"
|
||||
installedVersion=""
|
||||
key="com.mattermost.nps"
|
||||
name="User Satisfaction Surveys"
|
||||
version="1.0.3"
|
||||
/>
|
||||
<Connect(MarketplaceItemPlugin)
|
||||
description="This plugin sends quarterly user satisfaction surveys to gather feedback and help improve Mattermost"
|
||||
homepageUrl="https://github.com/mattermost/mattermost-plugin-nps"
|
||||
id="com.mattermost.nps"
|
||||
installedVersion=""
|
||||
key="com.mattermost.nps"
|
||||
name="User Satisfaction Surveys"
|
||||
version="1.0.3"
|
||||
/>
|
||||
<Connect(MarketplaceItemPlugin)
|
||||
description="This plugin sends quarterly user satisfaction surveys to gather feedback and help improve Mattermost"
|
||||
homepageUrl="https://github.com/mattermost/mattermost-plugin-nps"
|
||||
id="com.mattermost.nps"
|
||||
installedVersion=""
|
||||
key="com.mattermost.nps"
|
||||
name="User Satisfaction Surveys"
|
||||
version="1.0.3"
|
||||
/>
|
||||
<Connect(MarketplaceItemPlugin)
|
||||
description="This plugin sends quarterly user satisfaction surveys to gather feedback and help improve Mattermost"
|
||||
homepageUrl="https://github.com/mattermost/mattermost-plugin-nps"
|
||||
id="com.mattermost.nps"
|
||||
installedVersion=""
|
||||
key="com.mattermost.nps"
|
||||
name="User Satisfaction Surveys"
|
||||
version="1.0.3"
|
||||
/>
|
||||
<Connect(MarketplaceItemPlugin)
|
||||
description="This plugin sends quarterly user satisfaction surveys to gather feedback and help improve Mattermost"
|
||||
homepageUrl="https://github.com/mattermost/mattermost-plugin-nps"
|
||||
id="com.mattermost.nps"
|
||||
installedVersion=""
|
||||
key="com.mattermost.nps"
|
||||
name="User Satisfaction Surveys"
|
||||
version="1.0.3"
|
||||
/>
|
||||
<Connect(MarketplaceItemPlugin)
|
||||
description="This plugin sends quarterly user satisfaction surveys to gather feedback and help improve Mattermost"
|
||||
homepageUrl="https://github.com/mattermost/mattermost-plugin-nps"
|
||||
id="com.mattermost.nps"
|
||||
installedVersion=""
|
||||
key="com.mattermost.nps"
|
||||
name="User Satisfaction Surveys"
|
||||
version="1.0.3"
|
||||
/>
|
||||
<Connect(MarketplaceItemPlugin)
|
||||
description="This plugin sends quarterly user satisfaction surveys to gather feedback and help improve Mattermost"
|
||||
homepageUrl="https://github.com/mattermost/mattermost-plugin-nps"
|
||||
id="com.mattermost.nps"
|
||||
installedVersion=""
|
||||
key="com.mattermost.nps"
|
||||
name="User Satisfaction Surveys"
|
||||
version="1.0.3"
|
||||
/>
|
||||
<Connect(MarketplaceItemPlugin)
|
||||
description="This plugin sends quarterly user satisfaction surveys to gather feedback and help improve Mattermost"
|
||||
homepageUrl="https://github.com/mattermost/mattermost-plugin-nps"
|
||||
id="com.mattermost.nps"
|
||||
installedVersion=""
|
||||
key="com.mattermost.nps"
|
||||
name="User Satisfaction Surveys"
|
||||
version="1.0.3"
|
||||
/>
|
||||
<Connect(MarketplaceItemPlugin)
|
||||
description="This plugin sends quarterly user satisfaction surveys to gather feedback and help improve Mattermost"
|
||||
homepageUrl="https://github.com/mattermost/mattermost-plugin-nps"
|
||||
id="com.mattermost.nps"
|
||||
installedVersion=""
|
||||
key="com.mattermost.nps"
|
||||
name="User Satisfaction Surveys"
|
||||
version="1.0.3"
|
||||
/>
|
||||
<Connect(NavigationRow)
|
||||
maximumPerPage={15}
|
||||
onNextPageButtonClick={[Function]}
|
||||
onPreviousPageButtonClick={[Function]}
|
||||
page={0}
|
||||
total={17}
|
||||
<div
|
||||
className="no_plugins__message"
|
||||
/>
|
||||
</div>
|
||||
`;
|
||||
|
||||
@@ -8,8 +8,7 @@ import {AuthorType, MarketplacePlugin, ReleaseStage} from '@mattermost/types/mar
|
||||
|
||||
import MarketplaceItem from '../marketplace_item/marketplace_item_plugin';
|
||||
|
||||
import MarketplaceList from './marketplace_list';
|
||||
import NavigationRow from './navigation_row';
|
||||
import MarketplaceList, {ITEMS_PER_PAGE} from './marketplace_list';
|
||||
|
||||
describe('components/marketplace/marketplace_list', () => {
|
||||
const samplePlugin: MarketplacePlugin = {
|
||||
@@ -28,8 +27,20 @@ describe('components/marketplace/marketplace_list', () => {
|
||||
installed_version: '',
|
||||
};
|
||||
|
||||
it('should render with multiple plugins', () => {
|
||||
const wrapper = shallow<MarketplaceList>(
|
||||
it('should render default', () => {
|
||||
const wrapper = shallow(
|
||||
<MarketplaceList
|
||||
listing={[]}
|
||||
page={0}
|
||||
noResultsMessage=''
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should render page with ITEMS_PER_PAGE plugins', () => {
|
||||
const wrapper = shallow(
|
||||
<MarketplaceList
|
||||
listing={[
|
||||
samplePlugin, samplePlugin, samplePlugin, samplePlugin, samplePlugin,
|
||||
@@ -37,28 +48,29 @@ describe('components/marketplace/marketplace_list', () => {
|
||||
samplePlugin, samplePlugin, samplePlugin, samplePlugin, samplePlugin,
|
||||
samplePlugin, samplePlugin,
|
||||
]}
|
||||
page={0}
|
||||
noResultsMessage=''
|
||||
/>,
|
||||
);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
|
||||
expect(wrapper.state().page).toEqual(0);
|
||||
expect(wrapper.find(MarketplaceItem)).toHaveLength(15);
|
||||
expect(wrapper.find(NavigationRow)).toHaveLength(1);
|
||||
expect(wrapper.find(NavigationRow).props().page).toEqual(0);
|
||||
expect(wrapper.find(NavigationRow).props().total).toEqual(17);
|
||||
expect(wrapper.find(NavigationRow).props().maximumPerPage).toEqual(15);
|
||||
expect(wrapper.find(MarketplaceItem)).toHaveLength(ITEMS_PER_PAGE);
|
||||
});
|
||||
|
||||
it('should set page to 0 when list of plugins changed', () => {
|
||||
const wrapper = shallow<MarketplaceList>(
|
||||
it('should render no results', () => {
|
||||
const wrapper = shallow(
|
||||
<MarketplaceList
|
||||
listing={[samplePlugin, samplePlugin]}
|
||||
listing={[]}
|
||||
page={0}
|
||||
noResultsMessage='No plugins available'
|
||||
noResultsAction={{
|
||||
label: 'action',
|
||||
onClick: jest.fn(),
|
||||
}}
|
||||
/>,
|
||||
);
|
||||
|
||||
wrapper.setState({page: 10});
|
||||
wrapper.setProps({listing: [samplePlugin]});
|
||||
|
||||
expect(wrapper.state().page).toEqual(0);
|
||||
expect(wrapper.find('.icon__plugin').length).toEqual(1);
|
||||
expect(wrapper.find('.no_plugins__message').length).toEqual(1);
|
||||
expect(wrapper.find('.no_plugins__action').length).toEqual(1);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,107 +1,116 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import React from 'react';
|
||||
import React, {useCallback, useMemo} from 'react';
|
||||
import {useIntl} from 'react-intl';
|
||||
|
||||
import type {MarketplaceApp, MarketplacePlugin} from '@mattermost/types/marketplace';
|
||||
import {isPlugin, getName} from 'mattermost-redux/utils/marketplace';
|
||||
|
||||
import PluginIcon from 'components/widgets/icons/plugin_icon';
|
||||
|
||||
import MarketplaceItemPlugin from '../marketplace_item/marketplace_item_plugin';
|
||||
import MarketplaceItemApp from '../marketplace_item/marketplace_item_app';
|
||||
|
||||
import NavigationRow from './navigation_row';
|
||||
|
||||
const ITEMS_PER_PAGE = 15;
|
||||
export const ITEMS_PER_PAGE = 15;
|
||||
|
||||
type MarketplaceListProps = {
|
||||
listing: Array<MarketplacePlugin | MarketplaceApp>;
|
||||
};
|
||||
|
||||
type MarketplaceListState = {
|
||||
page: number;
|
||||
noResultsMessage: string;
|
||||
noResultsAction?: {
|
||||
label: string;
|
||||
onClick: () => void;
|
||||
};
|
||||
filter?: string;
|
||||
listRef?: React.RefObject<HTMLDivElement>;
|
||||
};
|
||||
|
||||
export default class MarketplaceList extends React.PureComponent <MarketplaceListProps, MarketplaceListState> {
|
||||
static getDerivedStateFromProps(props: MarketplaceListProps, state: MarketplaceListState): MarketplaceListState | null {
|
||||
if (state.page > 0 && props.listing.length < ITEMS_PER_PAGE) {
|
||||
return {page: 0};
|
||||
const MarketplaceList = ({
|
||||
listing,
|
||||
page,
|
||||
noResultsMessage,
|
||||
noResultsAction,
|
||||
filter,
|
||||
listRef,
|
||||
}: MarketplaceListProps) => {
|
||||
const {formatMessage} = useIntl();
|
||||
|
||||
const pageItems = useMemo(() => {
|
||||
if (listing.length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
constructor(props: MarketplaceListProps) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
page: 0,
|
||||
};
|
||||
}
|
||||
|
||||
nextPage = (): void => {
|
||||
this.setState((state) => ({
|
||||
page: state.page + 1,
|
||||
}));
|
||||
};
|
||||
|
||||
previousPage = (): void => {
|
||||
this.setState((state) => ({
|
||||
page: state.page - 1,
|
||||
}));
|
||||
};
|
||||
|
||||
render(): JSX.Element {
|
||||
const pageStart = this.state.page * ITEMS_PER_PAGE;
|
||||
const pageStart = page * ITEMS_PER_PAGE;
|
||||
const pageEnd = pageStart + ITEMS_PER_PAGE;
|
||||
|
||||
this.props.listing.sort((a, b) => {
|
||||
return getName(a).localeCompare(getName(b));
|
||||
});
|
||||
return [...listing].
|
||||
sort((a, b) => getName(a).localeCompare(getName(b))).
|
||||
slice(pageStart, pageEnd).
|
||||
map((i) => (
|
||||
isPlugin(i) ? (
|
||||
<MarketplaceItemPlugin
|
||||
key={i.manifest.id}
|
||||
id={i.manifest.id}
|
||||
name={i.manifest.name}
|
||||
description={i.manifest.description}
|
||||
version={i.manifest.version}
|
||||
homepageUrl={i.homepage_url}
|
||||
releaseNotesUrl={i.release_notes_url}
|
||||
labels={i.labels}
|
||||
iconData={i.icon_data}
|
||||
installedVersion={i.installed_version}
|
||||
/>
|
||||
) : (
|
||||
<MarketplaceItemApp
|
||||
key={i.manifest.app_id}
|
||||
id={i.manifest.app_id}
|
||||
name={i.manifest.display_name}
|
||||
description={i.manifest.description}
|
||||
homepageUrl={i.manifest.homepage_url}
|
||||
iconURL={i.icon_url}
|
||||
installed={i.installed}
|
||||
labels={i.labels}
|
||||
/>
|
||||
)
|
||||
));
|
||||
}, [listing, page]);
|
||||
|
||||
const itemsToDisplay = this.props.listing.slice(pageStart, pageEnd);
|
||||
const getNoResultsMessage = useCallback(() => (
|
||||
filter ? (
|
||||
formatMessage(
|
||||
{id: 'marketplace_modal_list.no_plugins_filter', defaultMessage: 'No results for "{filter}"'},
|
||||
{filter},
|
||||
)
|
||||
) : (
|
||||
noResultsMessage
|
||||
)
|
||||
), [filter, noResultsMessage]);
|
||||
|
||||
return (
|
||||
<div className='more-modal__list'>
|
||||
{itemsToDisplay.map((i) => {
|
||||
if (isPlugin(i)) {
|
||||
return (
|
||||
<MarketplaceItemPlugin
|
||||
key={i.manifest.id}
|
||||
id={i.manifest.id}
|
||||
name={i.manifest.name}
|
||||
description={i.manifest.description}
|
||||
version={i.manifest.version}
|
||||
homepageUrl={i.homepage_url}
|
||||
releaseNotesUrl={i.release_notes_url}
|
||||
labels={i.labels}
|
||||
iconData={i.icon_data}
|
||||
installedVersion={i.installed_version}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<MarketplaceItemApp
|
||||
key={i.manifest.app_id}
|
||||
id={i.manifest.app_id}
|
||||
name={i.manifest.display_name}
|
||||
description={i.manifest.description}
|
||||
homepageUrl={i.manifest.homepage_url}
|
||||
iconURL={i.icon_url}
|
||||
installed={i.installed}
|
||||
labels={i.labels}
|
||||
/>
|
||||
);
|
||||
})
|
||||
}
|
||||
<NavigationRow
|
||||
page={this.state.page}
|
||||
total={this.props.listing.length}
|
||||
maximumPerPage={ITEMS_PER_PAGE}
|
||||
onNextPageButtonClick={this.nextPage}
|
||||
onPreviousPageButtonClick={this.previousPage}
|
||||
/>
|
||||
return (listing.length === 0 ? (
|
||||
<div className='no_plugins'>
|
||||
<PluginIcon className='icon__plugin'/>
|
||||
<div className='no_plugins__message'>
|
||||
{getNoResultsMessage()}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
{noResultsAction && (
|
||||
<button
|
||||
className='no_plugins__action'
|
||||
onClick={noResultsAction.onClick}
|
||||
data-testid='Install-Plugins-button'
|
||||
>
|
||||
{noResultsAction.label}
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
) : (
|
||||
<div
|
||||
ref={listRef}
|
||||
className='more-modal__list'
|
||||
>
|
||||
{pageItems}
|
||||
</div>
|
||||
));
|
||||
};
|
||||
|
||||
export default MarketplaceList;
|
||||
|
||||
@@ -1,157 +0,0 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`components/marketplace/navigation_row should not render any buttons 1`] = `
|
||||
<div
|
||||
className="navigation-row"
|
||||
>
|
||||
<div
|
||||
className="col-xs-2"
|
||||
/>
|
||||
<div
|
||||
className="col-xs-8 count"
|
||||
style={
|
||||
Object {
|
||||
"color": "rgba(255,255,255,0.6)",
|
||||
}
|
||||
}
|
||||
>
|
||||
<MemoizedFormattedMessage
|
||||
defaultMessage="{startCount, number} - {endCount, number} {total, plural, one {plugin} other {plugins}} of {total, number} total"
|
||||
id="marketplace_list.count_total_page"
|
||||
values={
|
||||
Object {
|
||||
"endCount": 15,
|
||||
"startCount": 1,
|
||||
"total": 15,
|
||||
}
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
className="col-xs-2"
|
||||
/>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`components/marketplace/navigation_row should render next and previous buttons 1`] = `
|
||||
<div
|
||||
className="navigation-row"
|
||||
>
|
||||
<div
|
||||
className="col-xs-2"
|
||||
>
|
||||
<NavigationButton
|
||||
defaultMessage="Previous"
|
||||
messageId="more_channels.prev"
|
||||
onClick={[MockFunction]}
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
className="col-xs-8 count"
|
||||
style={
|
||||
Object {
|
||||
"color": "rgba(255,255,255,0.6)",
|
||||
}
|
||||
}
|
||||
>
|
||||
<MemoizedFormattedMessage
|
||||
defaultMessage="{startCount, number} - {endCount, number} {total, plural, one {plugin} other {plugins}} of {total, number} total"
|
||||
id="marketplace_list.count_total_page"
|
||||
values={
|
||||
Object {
|
||||
"endCount": 30,
|
||||
"startCount": 16,
|
||||
"total": 32,
|
||||
}
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
className="col-xs-2"
|
||||
>
|
||||
<NavigationButton
|
||||
defaultMessage="Next"
|
||||
messageId="more_channels.next"
|
||||
onClick={[MockFunction]}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`components/marketplace/navigation_row should render only next button 1`] = `
|
||||
<div
|
||||
className="navigation-row"
|
||||
>
|
||||
<div
|
||||
className="col-xs-2"
|
||||
/>
|
||||
<div
|
||||
className="col-xs-8 count"
|
||||
style={
|
||||
Object {
|
||||
"color": "rgba(255,255,255,0.6)",
|
||||
}
|
||||
}
|
||||
>
|
||||
<MemoizedFormattedMessage
|
||||
defaultMessage="{startCount, number} - {endCount, number} {total, plural, one {plugin} other {plugins}} of {total, number} total"
|
||||
id="marketplace_list.count_total_page"
|
||||
values={
|
||||
Object {
|
||||
"endCount": 15,
|
||||
"startCount": 1,
|
||||
"total": 32,
|
||||
}
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
className="col-xs-2"
|
||||
>
|
||||
<NavigationButton
|
||||
defaultMessage="Next"
|
||||
messageId="more_channels.next"
|
||||
onClick={[MockFunction]}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`components/marketplace/navigation_row should render only previous button 1`] = `
|
||||
<div
|
||||
className="navigation-row"
|
||||
>
|
||||
<div
|
||||
className="col-xs-2"
|
||||
>
|
||||
<NavigationButton
|
||||
defaultMessage="Previous"
|
||||
messageId="more_channels.prev"
|
||||
onClick={[MockFunction]}
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
className="col-xs-8 count"
|
||||
style={
|
||||
Object {
|
||||
"color": "rgba(255,255,255,0.6)",
|
||||
}
|
||||
}
|
||||
>
|
||||
<MemoizedFormattedMessage
|
||||
defaultMessage="{startCount, number} - {endCount, number} {total, plural, one {plugin} other {plugins}} of {total, number} total"
|
||||
id="marketplace_list.count_total_page"
|
||||
values={
|
||||
Object {
|
||||
"endCount": 32,
|
||||
"startCount": 31,
|
||||
"total": 32,
|
||||
}
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
className="col-xs-2"
|
||||
/>
|
||||
</div>
|
||||
`;
|
||||
@@ -1,17 +0,0 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {connect} from 'react-redux';
|
||||
|
||||
import {getTheme} from 'mattermost-redux/selectors/entities/preferences';
|
||||
import {GlobalState} from '@mattermost/types/store';
|
||||
|
||||
import NavigationRow from './navigation_row';
|
||||
|
||||
function mapStateToProps(state: GlobalState) {
|
||||
return {
|
||||
theme: getTheme(state),
|
||||
};
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps)(NavigationRow);
|
||||
@@ -1,33 +0,0 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import React from 'react';
|
||||
import {FormattedMessage} from 'react-intl';
|
||||
|
||||
type NavigationButtonProps = {
|
||||
onClick: (event: React.MouseEvent<HTMLButtonElement, MouseEvent>) => void;
|
||||
messageId: string;
|
||||
defaultMessage: string;
|
||||
};
|
||||
|
||||
export default class NavigationButton extends React.PureComponent <NavigationButtonProps> {
|
||||
onClick = (event: React.MouseEvent<HTMLButtonElement, MouseEvent>): void => {
|
||||
event.preventDefault();
|
||||
this.props.onClick(event);
|
||||
};
|
||||
|
||||
render(): JSX.Element {
|
||||
const {onClick, messageId, defaultMessage} = this.props;
|
||||
return (
|
||||
<button
|
||||
className='btn btn-link'
|
||||
onClick={onClick}
|
||||
>
|
||||
<FormattedMessage
|
||||
id={messageId}
|
||||
defaultMessage={defaultMessage}
|
||||
/>
|
||||
</button>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,73 +0,0 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import React from 'react';
|
||||
import {shallow} from 'enzyme';
|
||||
|
||||
import {Theme} from 'mattermost-redux/selectors/entities/preferences';
|
||||
|
||||
import NavigationRow, {NavigationRowProps} from './navigation_row';
|
||||
|
||||
describe('components/marketplace/navigation_row', () => {
|
||||
const baseProps: NavigationRowProps = {
|
||||
page: 0,
|
||||
total: 32,
|
||||
maximumPerPage: 15,
|
||||
onNextPageButtonClick: jest.fn(),
|
||||
onPreviousPageButtonClick: jest.fn(),
|
||||
theme: {centerChannelColor: '#fff'} as Theme,
|
||||
};
|
||||
|
||||
it('should render only next button', () => {
|
||||
const wrapper = shallow<NavigationRow>(
|
||||
<NavigationRow {...baseProps}/>,
|
||||
);
|
||||
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
expect(wrapper.find('NavigationButton')).toHaveLength(1);
|
||||
|
||||
wrapper.find('NavigationButton').simulate('click', {preventDefault: jest.fn});
|
||||
|
||||
expect(wrapper.instance().props.onNextPageButtonClick).toHaveBeenCalledTimes(1);
|
||||
expect(wrapper.instance().props.onPreviousPageButtonClick).toHaveBeenCalledTimes(0);
|
||||
});
|
||||
|
||||
it('should render next and previous buttons', () => {
|
||||
const props = {...baseProps, page: 1};
|
||||
const wrapper = shallow<NavigationRow>(
|
||||
<NavigationRow{...props}/>,
|
||||
);
|
||||
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
expect(wrapper.find('NavigationButton')).toHaveLength(2);
|
||||
|
||||
wrapper.find('NavigationButton').at(0).simulate('click', {preventDefault: jest.fn});
|
||||
wrapper.find('NavigationButton').at(1).simulate('click', {preventDefault: jest.fn});
|
||||
|
||||
expect(wrapper.instance().props.onNextPageButtonClick).toHaveBeenCalledTimes(1);
|
||||
expect(wrapper.instance().props.onPreviousPageButtonClick).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('should render only previous button', () => {
|
||||
const props = {...baseProps, page: 2};
|
||||
const wrapper = shallow<NavigationRow>(
|
||||
<NavigationRow{...props}/>,
|
||||
);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
expect(wrapper.find('NavigationButton')).toHaveLength(1);
|
||||
|
||||
wrapper.find('NavigationButton').simulate('click', {preventDefault: jest.fn});
|
||||
|
||||
expect(wrapper.instance().props.onNextPageButtonClick).toHaveBeenCalledTimes(0);
|
||||
expect(wrapper.instance().props.onPreviousPageButtonClick).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('should not render any buttons', () => {
|
||||
const props = {...baseProps, page: 0, total: 15};
|
||||
const wrapper = shallow<NavigationRow>(
|
||||
<NavigationRow{...props}/>,
|
||||
);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
expect(wrapper.find('NavigationButton')).toHaveLength(0);
|
||||
});
|
||||
});
|
||||
@@ -1,87 +0,0 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import React from 'react';
|
||||
import {FormattedMessage} from 'react-intl';
|
||||
|
||||
import {Theme} from 'mattermost-redux/selectors/entities/preferences';
|
||||
import {changeOpacity, makeStyleFromTheme} from 'mattermost-redux/utils/theme_utils';
|
||||
|
||||
import NavigationButton from './navigation_button';
|
||||
|
||||
export type NavigationRowProps = {
|
||||
page: number;
|
||||
total: number;
|
||||
maximumPerPage: number;
|
||||
onNextPageButtonClick: (event: React.MouseEvent<HTMLButtonElement, MouseEvent>) => void;
|
||||
onPreviousPageButtonClick: (event: React.MouseEvent<HTMLButtonElement, MouseEvent>) => void;
|
||||
theme: Theme;
|
||||
};
|
||||
|
||||
export default class NavigationRow extends React.PureComponent <NavigationRowProps> {
|
||||
canShowNextButton = (): boolean => {
|
||||
const {page, maximumPerPage, total} = this.props;
|
||||
const totalPages = Math.trunc((total - 1) / maximumPerPage);
|
||||
|
||||
return totalPages > page;
|
||||
};
|
||||
|
||||
renderCount = (): JSX.Element => {
|
||||
const {page, total, maximumPerPage} = this.props;
|
||||
const startCount = page * maximumPerPage;
|
||||
const endCount = Math.min(startCount + maximumPerPage, total);
|
||||
|
||||
return (
|
||||
<FormattedMessage
|
||||
id='marketplace_list.count_total_page'
|
||||
defaultMessage='{startCount, number} - {endCount, number} {total, plural, one {plugin} other {plugins}} of {total, number} total'
|
||||
values={{
|
||||
startCount: startCount + 1,
|
||||
endCount,
|
||||
total,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
render(): JSX.Element {
|
||||
const style = getStyle(this.props.theme);
|
||||
|
||||
return (
|
||||
<div className='navigation-row'>
|
||||
<div className='col-xs-2'>
|
||||
{(this.props.page > 0) && (
|
||||
<NavigationButton
|
||||
onClick={this.props.onPreviousPageButtonClick}
|
||||
messageId={'more_channels.prev'}
|
||||
defaultMessage={'Previous'}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
<div
|
||||
className='col-xs-8 count'
|
||||
style={style.count}
|
||||
>
|
||||
{this.renderCount()}
|
||||
</div>
|
||||
<div className='col-xs-2'>
|
||||
{this.canShowNextButton() && (
|
||||
<NavigationButton
|
||||
onClick={this.props.onNextPageButtonClick}
|
||||
messageId={'more_channels.next'}
|
||||
defaultMessage={'Next'}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const getStyle = makeStyleFromTheme((theme) => {
|
||||
return {
|
||||
count: {
|
||||
color: changeOpacity(theme.centerChannelColor, 0.6),
|
||||
},
|
||||
};
|
||||
});
|
||||
@@ -1,21 +1,11 @@
|
||||
@import 'utils/variables';
|
||||
@import 'utils/mixins';
|
||||
|
||||
.modal-marketplace {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding-top: 50px;
|
||||
color: var(--center-channel-color);
|
||||
font-size: 16px;
|
||||
|
||||
@media (max-width: 768px) {
|
||||
padding-right: 15px;
|
||||
padding-left: 15px;
|
||||
}
|
||||
.marketplace-modal {
|
||||
width: 800px;
|
||||
|
||||
div.navigation-row {
|
||||
overflow: unset;
|
||||
margin-top: 10px;
|
||||
|
||||
div {
|
||||
@@ -29,7 +19,7 @@
|
||||
}
|
||||
|
||||
div.count {
|
||||
padding-top: 8px;
|
||||
padding: 9px 15px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,33 +35,47 @@
|
||||
}
|
||||
|
||||
.nav-tabs {
|
||||
font-size: 14px;
|
||||
padding: 0 32px;
|
||||
margin: 0 0 8px;
|
||||
|
||||
> li {
|
||||
> a {
|
||||
padding: 10px 16px;
|
||||
li {
|
||||
margin-right: 0;
|
||||
|
||||
a {
|
||||
padding: 13px 12px;
|
||||
border: none;
|
||||
background: transparent;
|
||||
color: rgba(var(--center-channel-color-rgb), 0.64);
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
line-height: 20px;
|
||||
transition: all 0.15s ease;
|
||||
|
||||
&:hover,
|
||||
&:active,
|
||||
&:focus {
|
||||
&:focus,
|
||||
&:focus-within {
|
||||
border: none;
|
||||
background: transparent;
|
||||
border-radius: none;
|
||||
color: var(--center-channel-color);
|
||||
}
|
||||
}
|
||||
|
||||
&.active > a {
|
||||
color: var(--center-channel-color);
|
||||
&.active {
|
||||
border-bottom: 2px solid var(--denim-button-bg);
|
||||
|
||||
a {
|
||||
color: var(--denim-button-bg);
|
||||
}
|
||||
}
|
||||
|
||||
&:not(:first-child) {
|
||||
margin-left: 8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
h1 {
|
||||
margin: 8px 0 24px;
|
||||
font-size: 28px;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-weight: 300;
|
||||
|
||||
@@ -81,8 +85,14 @@
|
||||
}
|
||||
|
||||
.more-modal__list {
|
||||
height: 390px;
|
||||
margin: 0 6px 8px 12px;
|
||||
overflow-y: scroll;
|
||||
|
||||
.more-modal__row {
|
||||
align-items: normal;
|
||||
min-height: 80px;
|
||||
padding: 16px 20px;
|
||||
border-bottom: none;
|
||||
|
||||
.marketplace__tag {
|
||||
margin-left: 6px;
|
||||
@@ -94,25 +104,91 @@
|
||||
margin: 10px 10px 0 0;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
.more-modal__details {
|
||||
padding-left: 16px;
|
||||
|
||||
.more-modal__row--link {
|
||||
color: var(--center-channel-color);
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
line-height: 24px;
|
||||
}
|
||||
|
||||
.more-modal__description {
|
||||
margin: 2px 0 0;
|
||||
color: rgba(var(--center-channel-color-rgb), 0.64);
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
line-height: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.more-modal__actions {
|
||||
padding-left: 16px;
|
||||
margin: 0;
|
||||
|
||||
.plugin-configure,
|
||||
.app-installed {
|
||||
@include secondary-button;
|
||||
@include button-medium;
|
||||
}
|
||||
|
||||
.plugin-install,
|
||||
.app-install {
|
||||
@include primary-button;
|
||||
@include button-medium;
|
||||
}
|
||||
|
||||
a {
|
||||
&:hover,
|
||||
&:focus {
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&:hover,
|
||||
&:focus,
|
||||
&:focus-within {
|
||||
background-color: rgba(var(--center-channel-color-rgb), 0.08);
|
||||
}
|
||||
}
|
||||
|
||||
.more-modal__description {
|
||||
margin: 2px 0 0;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
.icon__plugin {
|
||||
display: flex;
|
||||
height: 48px;
|
||||
flex: 0 0 48px;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background-color: $white;
|
||||
border-radius: 50%;
|
||||
|
||||
padding-bottom: 80px;
|
||||
svg,
|
||||
img {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
}
|
||||
|
||||
svg {
|
||||
fill: var(--button-bg);
|
||||
}
|
||||
|
||||
img {
|
||||
border-radius: 4px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.search_input {
|
||||
width: 720px;
|
||||
height: 40px;
|
||||
flex: 1;
|
||||
margin-top: 28px;
|
||||
margin-right: 16px;
|
||||
margin-bottom: 20px;
|
||||
margin-left: 16px;
|
||||
box-shadow: none;
|
||||
.marketplace-modal-search {
|
||||
padding: 24px 0 0;
|
||||
|
||||
.search_input {
|
||||
width: 100%;
|
||||
border: 0 !important;
|
||||
border-radius: 0 !important;
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
|
||||
.btn {
|
||||
@@ -130,8 +206,9 @@
|
||||
|
||||
.tabs {
|
||||
display: flex;
|
||||
width: 720px;
|
||||
width: 100%;
|
||||
flex-direction: column;
|
||||
margin-top: 12px;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
@@ -160,67 +237,41 @@
|
||||
}
|
||||
}
|
||||
|
||||
.icon__plugin {
|
||||
.no_plugins {
|
||||
display: flex;
|
||||
height: 42px;
|
||||
flex: 0 0 42px;
|
||||
height: 390px;
|
||||
flex-flow: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-right: 4px;
|
||||
border-radius: 50%;
|
||||
margin-bottom: 8px;
|
||||
|
||||
svg {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
fill: var(--button-bg);
|
||||
&__message {
|
||||
margin-top: 20px;
|
||||
color: var(--center-channel-color);
|
||||
font-size: 20px;
|
||||
font-weight: 600;
|
||||
line-height: 28px;
|
||||
}
|
||||
}
|
||||
|
||||
.icon__plugin--background {
|
||||
padding: 6px;
|
||||
background-color: $white;
|
||||
&__action {
|
||||
@include primary-button;
|
||||
@include button-medium;
|
||||
|
||||
svg {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
margin-top: 30px;
|
||||
}
|
||||
}
|
||||
|
||||
.no_plugins_div {
|
||||
text-align: center;
|
||||
.icon__plugin {
|
||||
svg {
|
||||
fill: var(--button-bg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.item_error {
|
||||
background-color: rgba(var(--error-text-rgb), 0.08);
|
||||
background-color: rgba(var(--error-text-color-rgb), 0.08);
|
||||
}
|
||||
|
||||
.error_text {
|
||||
color: var(--error-text);
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.error-bar {
|
||||
position: fixed;
|
||||
z-index: 8;
|
||||
top: 0;
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
min-height: $announcement-bar-height;
|
||||
max-height: $announcement-bar-height;
|
||||
padding: 5px 30px;
|
||||
background-color: var(--center-channel-bg);
|
||||
color: var(--error-text);
|
||||
|
||||
.error-bar__content {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
display: flex;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background-color: rgba(var(--error-text-rgb), 0.12);
|
||||
.loading {
|
||||
height: 390px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,19 +5,21 @@ import React from 'react';
|
||||
import {shallow} from 'enzyme';
|
||||
|
||||
import {AuthorType, MarketplacePlugin, ReleaseStage} from '@mattermost/types/marketplace';
|
||||
import type {PluginStatusRedux} from '@mattermost/types/plugins';
|
||||
|
||||
import {trackEvent} from 'actions/telemetry_actions.jsx';
|
||||
import {ActionFunc} from 'mattermost-redux/types/actions';
|
||||
|
||||
import MarketplaceModal, {AllListing, InstalledListing, MarketplaceModalProps} from './marketplace_modal';
|
||||
import {GlobalState} from 'types/store';
|
||||
import {ModalIdentifiers} from 'utils/constants';
|
||||
|
||||
jest.mock('actions/telemetry_actions.jsx', () => {
|
||||
const original = jest.requireActual('actions/telemetry_actions.jsx');
|
||||
return {
|
||||
...original,
|
||||
trackEvent: jest.fn(),
|
||||
};
|
||||
});
|
||||
import MarketplaceModal, {OpenedFromType} from './marketplace_modal';
|
||||
|
||||
let mockState: GlobalState;
|
||||
|
||||
jest.mock('react-redux', () => ({
|
||||
...jest.requireActual('react-redux') as typeof import('react-redux'),
|
||||
useSelector: (selector: (state: typeof mockState) => unknown) => selector(mockState),
|
||||
useDispatch: jest.fn(() => (action: ActionFunc) => action),
|
||||
}));
|
||||
|
||||
describe('components/marketplace/', () => {
|
||||
const samplePlugin: MarketplacePlugin = {
|
||||
@@ -52,161 +54,106 @@ describe('components/marketplace/', () => {
|
||||
installed_version: '1.0.3',
|
||||
};
|
||||
|
||||
describe('AllListing', () => {
|
||||
it('should render with no plugins', () => {
|
||||
const wrapper = shallow(
|
||||
<AllListing listing={[]}/>,
|
||||
);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
const defaultProps = {
|
||||
openedFrom: 'actions_menu' as OpenedFromType,
|
||||
};
|
||||
|
||||
it('should render with one plugin', () => {
|
||||
const wrapper = shallow(
|
||||
<AllListing listing={[samplePlugin]}/>,
|
||||
);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should render with plugins', () => {
|
||||
const wrapper = shallow(
|
||||
<AllListing listing={[samplePlugin, sampleInstalledPlugin]}/>,
|
||||
);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
describe('InstalledPlugins', () => {
|
||||
const baseProps = {
|
||||
changeTab: jest.fn(),
|
||||
};
|
||||
|
||||
it('should render with no plugins', () => {
|
||||
const wrapper = shallow(
|
||||
<InstalledListing
|
||||
{...baseProps}
|
||||
installedItems={[]}
|
||||
/>,
|
||||
);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should render with one plugin', () => {
|
||||
const wrapper = shallow(
|
||||
<InstalledListing
|
||||
{...baseProps}
|
||||
installedItems={[sampleInstalledPlugin]}
|
||||
/>,
|
||||
);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should render with multiple plugins', () => {
|
||||
const wrapper = shallow(
|
||||
<InstalledListing
|
||||
{...baseProps}
|
||||
installedItems={[sampleInstalledPlugin, sampleInstalledPlugin]}
|
||||
/>,
|
||||
);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
describe('MarketplaceModal', () => {
|
||||
const baseProps: MarketplaceModalProps = {
|
||||
show: true,
|
||||
listing: [samplePlugin],
|
||||
installedListing: [],
|
||||
pluginStatuses: {},
|
||||
siteURL: 'http://example.com',
|
||||
firstAdminVisitMarketplaceStatus: false,
|
||||
openedFrom: 'actions_menu',
|
||||
actions: {
|
||||
closeModal: jest.fn(),
|
||||
fetchListing: jest.fn(() => {
|
||||
return Promise.resolve({});
|
||||
}),
|
||||
filterListing: jest.fn(() => {
|
||||
return Promise.resolve({});
|
||||
}),
|
||||
setFirstAdminVisitMarketplaceStatus: jest.fn(),
|
||||
getPluginStatuses: jest.fn(),
|
||||
beforeEach(() => {
|
||||
mockState = {
|
||||
views: {
|
||||
modals: {
|
||||
modalState: {
|
||||
[ModalIdentifiers.PLUGIN_MARKETPLACE]: {
|
||||
open: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
marketplace: {
|
||||
plugins: [],
|
||||
apps: [],
|
||||
},
|
||||
},
|
||||
};
|
||||
entities: {
|
||||
general: {
|
||||
firstAdminCompleteSetup: false,
|
||||
},
|
||||
admin: {
|
||||
pluginStatuses: {},
|
||||
},
|
||||
},
|
||||
} as unknown as GlobalState;
|
||||
});
|
||||
|
||||
test('should render with no plugins installed', () => {
|
||||
const wrapper = shallow<MarketplaceModal>(
|
||||
<MarketplaceModal {...baseProps}/>,
|
||||
);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
test('should render default', () => {
|
||||
const wrapper = shallow(
|
||||
<MarketplaceModal {...defaultProps}/>,
|
||||
);
|
||||
|
||||
test('should render with plugins installed', () => {
|
||||
const props = {
|
||||
...baseProps,
|
||||
plugins: [
|
||||
...baseProps.listing,
|
||||
sampleInstalledPlugin,
|
||||
],
|
||||
installedListing: [
|
||||
sampleInstalledPlugin,
|
||||
],
|
||||
};
|
||||
expect(wrapper.shallow()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
const wrapper = shallow<MarketplaceModal>(
|
||||
<MarketplaceModal {...props}/>,
|
||||
);
|
||||
test('should render with no plugins available', () => {
|
||||
const setState = jest.fn();
|
||||
const useStateSpy = jest.spyOn(React, 'useState');
|
||||
useStateSpy.mockImplementationOnce(() => [false, setState]);
|
||||
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
const wrapper = shallow(
|
||||
<MarketplaceModal {...defaultProps}/>,
|
||||
);
|
||||
|
||||
test('should fetch plugins when plugin status is changed', () => {
|
||||
const fetchListing = baseProps.actions.fetchListing;
|
||||
const wrapper = shallow<MarketplaceModal>(<MarketplaceModal {...baseProps}/>);
|
||||
wrapper.update();
|
||||
|
||||
expect(fetchListing).toBeCalledTimes(1);
|
||||
wrapper.setProps({...baseProps});
|
||||
expect(fetchListing).toBeCalledTimes(1);
|
||||
expect(wrapper.shallow()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
const status = {
|
||||
id: 'test',
|
||||
} as PluginStatusRedux;
|
||||
wrapper.setProps({...baseProps, pluginStatuses: {test: status}});
|
||||
expect(fetchListing).toBeCalledTimes(2);
|
||||
});
|
||||
test('should render with plugins available', () => {
|
||||
const setState = jest.fn();
|
||||
const useStateSpy = jest.spyOn(React, 'useState');
|
||||
useStateSpy.mockImplementationOnce(() => [false, setState]);
|
||||
|
||||
test('should render with error banner', () => {
|
||||
const wrapper = shallow<MarketplaceModal>(
|
||||
<MarketplaceModal {...baseProps}/>,
|
||||
);
|
||||
mockState.views.marketplace.plugins = [
|
||||
samplePlugin,
|
||||
];
|
||||
|
||||
wrapper.setState({serverError: {name: 'some.error', message: 'Error test'}});
|
||||
const wrapper = shallow(
|
||||
<MarketplaceModal {...defaultProps}/>,
|
||||
);
|
||||
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
wrapper.update();
|
||||
|
||||
test('Should call for track event when searching', () => {
|
||||
const wrapper = shallow<MarketplaceModal>(
|
||||
<MarketplaceModal {...baseProps}/>,
|
||||
);
|
||||
expect(wrapper.shallow()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
wrapper.setState({filter: 'nps'});
|
||||
wrapper.instance().doSearch();
|
||||
test('should render with plugins installed', () => {
|
||||
const setState = jest.fn();
|
||||
const useStateSpy = jest.spyOn(React, 'useState');
|
||||
useStateSpy.mockImplementationOnce(() => [false, setState]);
|
||||
|
||||
expect(trackEvent).toHaveBeenCalledWith('plugins', 'ui_marketplace_opened', {from: 'actions_menu'});
|
||||
expect(trackEvent).toHaveBeenCalledWith('plugins', 'ui_marketplace_search', {filter: 'nps'});
|
||||
});
|
||||
mockState.views.marketplace.plugins = [
|
||||
samplePlugin,
|
||||
sampleInstalledPlugin,
|
||||
];
|
||||
|
||||
test('Should call for opened track event on mount', () => {
|
||||
const openedFrom = 'actions_menu';
|
||||
const wrapper = shallow(
|
||||
<MarketplaceModal {...defaultProps}/>,
|
||||
);
|
||||
|
||||
shallow<MarketplaceModal>(
|
||||
<MarketplaceModal
|
||||
{...baseProps}
|
||||
openedFrom={openedFrom}
|
||||
/>,
|
||||
);
|
||||
wrapper.update();
|
||||
|
||||
expect(trackEvent).toHaveBeenCalledWith('plugins', 'ui_marketplace_opened', {from: openedFrom});
|
||||
});
|
||||
expect(wrapper.shallow()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('should render with error banner', () => {
|
||||
const setState = jest.fn();
|
||||
const useStateSpy = jest.spyOn(React, 'useState');
|
||||
useStateSpy.mockImplementation(() => [true, setState]);
|
||||
|
||||
const wrapper = shallow(
|
||||
<MarketplaceModal {...defaultProps}/>,
|
||||
);
|
||||
|
||||
wrapper.update();
|
||||
|
||||
expect(wrapper.shallow()).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,283 +1,269 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import React from 'react';
|
||||
import {FormattedMessage} from 'react-intl';
|
||||
import debounce from 'lodash/debounce';
|
||||
import React, {useCallback, useEffect, useRef, useState} from 'react';
|
||||
import {Tabs, Tab, SelectCallback} from 'react-bootstrap';
|
||||
import {useIntl} from 'react-intl';
|
||||
import {useDispatch, useSelector} from 'react-redux';
|
||||
import {Link} from 'react-router-dom';
|
||||
import debounce from 'lodash/debounce';
|
||||
|
||||
import {PluginStatusRedux} from '@mattermost/types/plugins';
|
||||
import type {MarketplaceApp, MarketplacePlugin} from '@mattermost/types/marketplace';
|
||||
import {MagnifyIcon} from '@mattermost/compass-icons/components';
|
||||
|
||||
import FullScreenModal from 'components/widgets/modals/full_screen_modal';
|
||||
import RootPortal from 'components/root_portal';
|
||||
import QuickInput from 'components/quick_input';
|
||||
import LocalizedInput from 'components/localized_input/localized_input';
|
||||
import PluginIcon from 'components/widgets/icons/plugin_icon';
|
||||
import LoadingScreen from 'components/loading_screen';
|
||||
import FormattedMarkdownMessage from 'components/formatted_markdown_message';
|
||||
import {FooterPagination} from '@mattermost/components';
|
||||
import {getPluginStatuses} from 'mattermost-redux/actions/admin';
|
||||
import {setFirstAdminVisitMarketplaceStatus} from 'mattermost-redux/actions/general';
|
||||
import {getFirstAdminVisitMarketplaceStatus} from 'mattermost-redux/selectors/entities/general';
|
||||
import {ActionResult} from 'mattermost-redux/types/actions';
|
||||
|
||||
import {fetchListing, filterListing} from 'actions/marketplace';
|
||||
import {trackEvent} from 'actions/telemetry_actions.jsx';
|
||||
import {t} from 'utils/i18n';
|
||||
import {localizeMessage} from 'utils/utils';
|
||||
import {closeModal} from 'actions/views/modals';
|
||||
|
||||
import GenericModal from 'components/generic_modal';
|
||||
import LoadingScreen from 'components/loading_screen';
|
||||
import Input, {SIZE} from 'components/widgets/inputs/input/input';
|
||||
|
||||
import {getListing, getInstalledListing} from 'selectors/views/marketplace';
|
||||
import {isModalOpen} from 'selectors/views/modals';
|
||||
import {GlobalState} from 'types/store';
|
||||
import {ModalIdentifiers} from 'utils/constants';
|
||||
|
||||
import './marketplace_modal.scss';
|
||||
import MarketplaceList from './marketplace_list/marketplace_list';
|
||||
|
||||
import MarketplaceList, {ITEMS_PER_PAGE} from './marketplace_list/marketplace_list';
|
||||
|
||||
const MarketplaceTabs = {
|
||||
ALL_LISTING: 'allListing',
|
||||
ALL_LISTING: 'all',
|
||||
INSTALLED_LISTING: 'installed',
|
||||
};
|
||||
|
||||
const SEARCH_TIMEOUT_MILLISECONDS = 200;
|
||||
|
||||
const linkConsole = (msg: string) => (
|
||||
<Link to='/admin_console/plugins/plugin_management'>
|
||||
{msg}
|
||||
</Link>
|
||||
);
|
||||
|
||||
export type OpenedFromType = 'actions_menu' | 'app_bar' | 'channel_header' | 'command' | 'open_plugin_install_post' | 'product_menu';
|
||||
|
||||
type AllListingProps = {
|
||||
listing: Array<MarketplacePlugin | MarketplaceApp>;
|
||||
};
|
||||
|
||||
// AllListing renders the contents of the all listing tab.
|
||||
export const AllListing = ({listing}: AllListingProps): JSX.Element => {
|
||||
if (listing.length === 0) {
|
||||
return (
|
||||
<div className='no_plugins_div'>
|
||||
<br/>
|
||||
<PluginIcon className='icon__plugin'/>
|
||||
<div className='mt-3 light'>
|
||||
<FormattedMessage
|
||||
id='marketplace_modal.no_plugins'
|
||||
defaultMessage='There are no plugins available at this time.'
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return <MarketplaceList listing={listing}/>;
|
||||
};
|
||||
|
||||
type InstalledListingProps = {
|
||||
installedItems: Array<MarketplacePlugin | MarketplaceApp>;
|
||||
changeTab: SelectCallback;
|
||||
};
|
||||
|
||||
// InstalledListing renders the contents of the installed listing tab.
|
||||
export const InstalledListing = ({installedItems, changeTab}: InstalledListingProps): JSX.Element => {
|
||||
if (installedItems.length === 0) {
|
||||
return (
|
||||
<div className='no_plugins_div'>
|
||||
<br/>
|
||||
<PluginIcon className='icon__plugin'/>
|
||||
<div className='mt-3 light'>
|
||||
<FormattedMessage
|
||||
id='marketplace_modal.no_plugins_installed'
|
||||
defaultMessage='You do not have any plugins installed.'
|
||||
/>
|
||||
</div>
|
||||
<button
|
||||
className='mt-5 style--none color--link'
|
||||
onClick={() => changeTab(MarketplaceTabs.ALL_LISTING)}
|
||||
data-testid='Install-Plugins-button'
|
||||
>
|
||||
<FormattedMessage
|
||||
id='marketplace_modal.install_plugins'
|
||||
defaultMessage='Install Plugins'
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return <MarketplaceList listing={installedItems}/>;
|
||||
};
|
||||
|
||||
export type MarketplaceModalProps = {
|
||||
show: boolean;
|
||||
listing: Array<MarketplacePlugin | MarketplaceApp>;
|
||||
installedListing: Array<MarketplacePlugin | MarketplaceApp>;
|
||||
siteURL: string;
|
||||
pluginStatuses?: Record<string, PluginStatusRedux>;
|
||||
firstAdminVisitMarketplaceStatus: boolean;
|
||||
type MarketplaceModalProps = {
|
||||
openedFrom: OpenedFromType;
|
||||
actions: {
|
||||
closeModal: () => void;
|
||||
fetchListing(localOnly?: boolean): Promise<{error?: Error}>;
|
||||
filterListing(filter: string): Promise<{error?: Error}>;
|
||||
setFirstAdminVisitMarketplaceStatus(): Promise<void>;
|
||||
getPluginStatuses(): Promise<void>;
|
||||
};
|
||||
};
|
||||
|
||||
type MarketplaceModalState = {
|
||||
tabKey: unknown;
|
||||
loading: boolean;
|
||||
serverError?: Error;
|
||||
filter: string;
|
||||
};
|
||||
|
||||
// MarketplaceModal is the marketplace modal.
|
||||
export default class MarketplaceModal extends React.PureComponent<MarketplaceModalProps, MarketplaceModalState> {
|
||||
private filterRef: React.RefObject<HTMLInputElement>;
|
||||
|
||||
constructor(props: MarketplaceModalProps) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
tabKey: MarketplaceTabs.ALL_LISTING,
|
||||
loading: true,
|
||||
serverError: undefined,
|
||||
filter: '',
|
||||
};
|
||||
|
||||
this.filterRef = React.createRef();
|
||||
}
|
||||
|
||||
componentDidMount(): void {
|
||||
trackEvent('plugins', 'ui_marketplace_opened', {from: this.props.openedFrom});
|
||||
|
||||
this.fetchListing();
|
||||
this.props.actions.getPluginStatuses();
|
||||
if (!this.props.firstAdminVisitMarketplaceStatus) {
|
||||
trackEvent('plugins', 'ui_first_admin_visit_marketplace_status');
|
||||
|
||||
this.props.actions.setFirstAdminVisitMarketplaceStatus();
|
||||
}
|
||||
|
||||
this.filterRef.current?.focus();
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps: MarketplaceModalProps): void {
|
||||
// Automatically refresh the component when a plugin is installed or uninstalled.
|
||||
if (this.props.pluginStatuses !== prevProps.pluginStatuses) {
|
||||
this.fetchListing();
|
||||
}
|
||||
}
|
||||
|
||||
fetchListing = async (): Promise<void> => {
|
||||
const {error} = await this.props.actions.fetchListing();
|
||||
this.setState({loading: false, serverError: error});
|
||||
}
|
||||
|
||||
close = (): void => {
|
||||
trackEvent('plugins', 'ui_marketplace_closed');
|
||||
this.props.actions.closeModal();
|
||||
}
|
||||
|
||||
changeTab: SelectCallback = (tabKey: any): void => {
|
||||
this.setState({tabKey});
|
||||
}
|
||||
|
||||
onInput = (): void => {
|
||||
if (this.filterRef.current) {
|
||||
this.setState({filter: this.filterRef.current.value});
|
||||
|
||||
this.debouncedSearch();
|
||||
}
|
||||
}
|
||||
|
||||
handleClearSearch = (): void => {
|
||||
if (this.filterRef.current) {
|
||||
this.filterRef.current.value = '';
|
||||
this.setState({filter: this.filterRef.current.value}, this.doSearch);
|
||||
}
|
||||
}
|
||||
|
||||
doSearch = async (): Promise<void> => {
|
||||
trackEvent('plugins', 'ui_marketplace_search', {filter: this.state.filter});
|
||||
|
||||
const {error} = await this.props.actions.filterListing(this.state.filter);
|
||||
|
||||
this.setState({serverError: error});
|
||||
}
|
||||
|
||||
debouncedSearch = debounce(this.doSearch, SEARCH_TIMEOUT_MILLISECONDS);
|
||||
|
||||
render(): JSX.Element {
|
||||
const input = (
|
||||
<div className='filter-row filter-row--full'>
|
||||
<div className='col-sm-12'>
|
||||
<QuickInput
|
||||
id='searchMarketplaceTextbox'
|
||||
ref={this.filterRef}
|
||||
className='form-control filter-textbox search_input'
|
||||
placeholder={{id: t('marketplace_modal.search'), defaultMessage: 'Search Marketplace'}}
|
||||
inputComponent={LocalizedInput}
|
||||
onInput={this.onInput}
|
||||
value={this.state.filter}
|
||||
clearable={true}
|
||||
onClear={this.handleClearSearch}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
let errorBanner = null;
|
||||
if (this.state.serverError) {
|
||||
errorBanner = (
|
||||
<div
|
||||
className='error-bar'
|
||||
id='error_bar'
|
||||
>
|
||||
<div className='error-bar__content'>
|
||||
<FormattedMarkdownMessage
|
||||
id='app.plugin.marketplace_plugins.app_error'
|
||||
defaultMessage='Error connecting to the marketplace server. Please check your settings in the [System Console]({siteURL}/admin_console/plugins/plugin_management).'
|
||||
values={{siteURL: this.props.siteURL}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<RootPortal>
|
||||
<FullScreenModal
|
||||
show={this.props.show}
|
||||
onClose={this.close}
|
||||
ariaLabel={localizeMessage('marketplace_modal.title', 'Marketplace')}
|
||||
>
|
||||
{errorBanner}
|
||||
<div
|
||||
className='modal-marketplace'
|
||||
id='modal_marketplace'
|
||||
>
|
||||
<h1>
|
||||
<strong>
|
||||
<FormattedMessage
|
||||
id='marketplace_modal.title'
|
||||
defaultMessage='Marketplace'
|
||||
/>
|
||||
</strong>
|
||||
</h1>
|
||||
{input}
|
||||
<Tabs
|
||||
id='marketplaceTabs'
|
||||
className='tabs'
|
||||
defaultActiveKey={MarketplaceTabs.ALL_LISTING}
|
||||
activeKey={this.state.tabKey}
|
||||
onSelect={this.changeTab}
|
||||
unmountOnExit={true}
|
||||
>
|
||||
<Tab
|
||||
eventKey={MarketplaceTabs.ALL_LISTING}
|
||||
title={localizeMessage('marketplace_modal.tabs.all_listing', 'All')}
|
||||
>
|
||||
{this.state.loading ? <LoadingScreen/> : <AllListing listing={this.props.listing}/>}
|
||||
</Tab>
|
||||
<Tab
|
||||
eventKey={MarketplaceTabs.INSTALLED_LISTING}
|
||||
title={localizeMessage('marketplace_modal.tabs.installed_listing', 'Installed') + ` (${this.props.installedListing.length})`}
|
||||
>
|
||||
<InstalledListing
|
||||
installedItems={this.props.installedListing}
|
||||
changeTab={this.changeTab}
|
||||
/>
|
||||
</Tab>
|
||||
</Tabs>
|
||||
</div>
|
||||
</FullScreenModal>
|
||||
</RootPortal>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const MarketplaceModal = ({
|
||||
openedFrom,
|
||||
}: MarketplaceModalProps) => {
|
||||
const dispatch = useDispatch();
|
||||
const {formatMessage} = useIntl();
|
||||
const listRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
const show = useSelector((state: GlobalState) => isModalOpen(state, ModalIdentifiers.PLUGIN_MARKETPLACE));
|
||||
const listing = useSelector(getListing);
|
||||
const installedListing = useSelector(getInstalledListing);
|
||||
const pluginStatuses = useSelector((state: GlobalState) => state.entities.admin.pluginStatuses);
|
||||
const hasFirstAdminVisitedMarketplace = useSelector(getFirstAdminVisitMarketplaceStatus);
|
||||
|
||||
const [tabKey, setTabKey] = useState(MarketplaceTabs.ALL_LISTING);
|
||||
const [filter, setFilter] = useState('');
|
||||
const [page, setPage] = useState(0);
|
||||
const [hasLoaded, setHasLoaded] = useState(false);
|
||||
const [loading, setLoading] = React.useState(true);
|
||||
const [serverError, setServerError] = React.useState(false);
|
||||
|
||||
const doFetchListing = useCallback(async () => {
|
||||
const {error} = await dispatch(fetchListing()) as ActionResult;
|
||||
|
||||
if (error) {
|
||||
setServerError(true);
|
||||
}
|
||||
|
||||
setLoading(false);
|
||||
}, []);
|
||||
|
||||
const doSearch = useCallback(async () => {
|
||||
trackEvent('plugins', 'ui_marketplace_search', {filter});
|
||||
|
||||
const {error} = await dispatch(filterListing(filter)) as ActionResult;
|
||||
|
||||
if (error) {
|
||||
setServerError(true);
|
||||
}
|
||||
}, [filter]);
|
||||
|
||||
const debouncedSearch = debounce(doSearch, SEARCH_TIMEOUT_MILLISECONDS);
|
||||
|
||||
useEffect(() => {
|
||||
async function doFetch() {
|
||||
await dispatch(getPluginStatuses());
|
||||
await doFetchListing();
|
||||
setHasLoaded(true);
|
||||
}
|
||||
|
||||
trackEvent('plugins', 'ui_marketplace_opened', {from: openedFrom});
|
||||
|
||||
if (!hasFirstAdminVisitedMarketplace) {
|
||||
trackEvent('plugins', 'ui_first_admin_visit_marketplace_status');
|
||||
dispatch(setFirstAdminVisitMarketplaceStatus());
|
||||
}
|
||||
|
||||
doFetch();
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (hasLoaded) {
|
||||
doFetchListing();
|
||||
}
|
||||
}, [pluginStatuses]);
|
||||
|
||||
useEffect(() => {
|
||||
if (hasLoaded) {
|
||||
debouncedSearch();
|
||||
setPage(0);
|
||||
}
|
||||
}, [filter]);
|
||||
|
||||
const scrollListToTop = useCallback(() => {
|
||||
if (listRef.current) {
|
||||
listRef.current.scrollTop = 0;
|
||||
}
|
||||
}, []);
|
||||
|
||||
const handleOnClose = () => {
|
||||
trackEvent('plugins', 'ui_marketplace_closed');
|
||||
dispatch(closeModal(ModalIdentifiers.PLUGIN_MARKETPLACE));
|
||||
};
|
||||
|
||||
const handleChangeTab: SelectCallback = useCallback((tabKey) => {
|
||||
setTabKey(tabKey);
|
||||
setPage(0);
|
||||
scrollListToTop();
|
||||
}, [scrollListToTop]);
|
||||
|
||||
const handleOnChange = useCallback((event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setFilter(event.target.value);
|
||||
}, []);
|
||||
|
||||
const handleOnClear = useCallback(() => {
|
||||
setFilter('');
|
||||
}, []);
|
||||
|
||||
const handleOnNextPage = useCallback(() => {
|
||||
setPage(page + 1);
|
||||
scrollListToTop();
|
||||
}, [page, scrollListToTop]);
|
||||
|
||||
const handleOnPreviousPage = useCallback(() => {
|
||||
setPage(page - 1);
|
||||
scrollListToTop();
|
||||
}, [page, scrollListToTop]);
|
||||
|
||||
const handleNoResultsButtonClick = useCallback(() => {
|
||||
handleChangeTab(MarketplaceTabs.ALL_LISTING);
|
||||
}, [handleChangeTab]);
|
||||
|
||||
const getHeaderInput = useCallback(() => (
|
||||
<Input
|
||||
id='searchMarketplaceTextbox'
|
||||
name='searchMarketplaceTextbox'
|
||||
containerClassName='marketplace-modal-search'
|
||||
inputClassName='search_input'
|
||||
type='text'
|
||||
inputSize={SIZE.LARGE}
|
||||
inputPrefix={<MagnifyIcon size={24}/>}
|
||||
placeholder={formatMessage({id: 'marketplace_modal.search', defaultMessage: 'Search marketplace'})}
|
||||
useLegend={false}
|
||||
autoFocus={true}
|
||||
clearable={true}
|
||||
value={filter}
|
||||
onChange={handleOnChange}
|
||||
onClear={handleOnClear}
|
||||
/>
|
||||
), [filter, handleOnChange, handleOnClear]);
|
||||
|
||||
const getFooterContent = useCallback(() => (
|
||||
<FooterPagination
|
||||
page={page}
|
||||
total={tabKey === MarketplaceTabs.ALL_LISTING ? listing.length : installedListing.length}
|
||||
itemsPerPage={ITEMS_PER_PAGE}
|
||||
onNextPage={handleOnNextPage}
|
||||
onPreviousPage={handleOnPreviousPage}
|
||||
/>
|
||||
), [installedListing.length, listing.length, page, handleOnNextPage, handleOnPreviousPage, tabKey]);
|
||||
|
||||
return (
|
||||
<GenericModal
|
||||
id='marketplace-modal'
|
||||
className='marketplace-modal'
|
||||
modalHeaderText={formatMessage({id: 'marketplace_modal.title', defaultMessage: 'App Marketplace'})}
|
||||
ariaLabel={formatMessage({id: 'marketplace_modal.title', defaultMessage: 'App Marketplace'})}
|
||||
errorText={serverError ? (
|
||||
formatMessage(
|
||||
{
|
||||
id: 'marketplace_modal.app_error',
|
||||
defaultMessage: 'Error connecting to the marketplace server. Please check your settings in the <linkConsole>System Console</linkConsole>.',
|
||||
},
|
||||
{linkConsole},
|
||||
)
|
||||
) : undefined}
|
||||
show={show}
|
||||
compassDesign={true}
|
||||
bodyPadding={false}
|
||||
footerDivider={true}
|
||||
onExited={handleOnClose}
|
||||
footerContent={getFooterContent()}
|
||||
headerInput={getHeaderInput()}
|
||||
>
|
||||
<Tabs
|
||||
id='marketplaceTabs'
|
||||
className='tabs'
|
||||
defaultActiveKey={MarketplaceTabs.ALL_LISTING}
|
||||
activeKey={tabKey}
|
||||
onSelect={handleChangeTab}
|
||||
unmountOnExit={true}
|
||||
>
|
||||
<Tab
|
||||
eventKey={MarketplaceTabs.ALL_LISTING}
|
||||
title={formatMessage({id: 'marketplace_modal.tabs.all_listing', defaultMessage: 'All'})}
|
||||
>
|
||||
{loading ? (
|
||||
<LoadingScreen className='loading'/>
|
||||
) : (
|
||||
<MarketplaceList
|
||||
listRef={listRef}
|
||||
listing={listing}
|
||||
page={page}
|
||||
filter={filter}
|
||||
noResultsMessage={formatMessage({id: 'marketplace_modal.no_plugins', defaultMessage: 'No plugins found'})}
|
||||
/>
|
||||
)}
|
||||
</Tab>
|
||||
<Tab
|
||||
eventKey={MarketplaceTabs.INSTALLED_LISTING}
|
||||
title={formatMessage(
|
||||
{id: 'marketplace_modal.tabs.installed_listing', defaultMessage: 'Installed ({count})'},
|
||||
{count: installedListing.length},
|
||||
)}
|
||||
>
|
||||
<MarketplaceList
|
||||
listRef={listRef}
|
||||
listing={installedListing}
|
||||
page={page}
|
||||
filter={filter}
|
||||
noResultsMessage={formatMessage({
|
||||
id: 'marketplace_modal.no_plugins_installed',
|
||||
defaultMessage: 'No plugins installed found',
|
||||
})}
|
||||
noResultsAction={{
|
||||
label: formatMessage({id: 'marketplace_modal.install_plugins', defaultMessage: 'Install plugins'}),
|
||||
onClick: handleNoResultsButtonClick,
|
||||
}}
|
||||
/>
|
||||
</Tab>
|
||||
</Tabs>
|
||||
</GenericModal>
|
||||
);
|
||||
};
|
||||
|
||||
export default MarketplaceModal;
|
||||
|
||||
@@ -4,6 +4,7 @@ exports[`ProductNoticesModal Match snapshot for single notice 1`] = `
|
||||
<GenericModal
|
||||
autoCloseOnCancelButton={false}
|
||||
autoCloseOnConfirmButton={true}
|
||||
bodyPadding={true}
|
||||
cancelButtonText={null}
|
||||
className="productNotices"
|
||||
confirmButtonText={
|
||||
@@ -44,6 +45,7 @@ exports[`ProductNoticesModal Match snapshot for user notice 1`] = `
|
||||
<GenericModal
|
||||
autoCloseOnCancelButton={false}
|
||||
autoCloseOnConfirmButton={true}
|
||||
bodyPadding={true}
|
||||
cancelButtonText={
|
||||
<React.Fragment>
|
||||
<PreviousIcon
|
||||
@@ -122,6 +124,7 @@ exports[`ProductNoticesModal Should match snapshot for system admin notice 1`] =
|
||||
<GenericModal
|
||||
autoCloseOnCancelButton={false}
|
||||
autoCloseOnConfirmButton={false}
|
||||
bodyPadding={true}
|
||||
cancelButtonText={null}
|
||||
className="productNotices"
|
||||
confirmButtonText={
|
||||
|
||||
@@ -11,6 +11,7 @@ exports[`components/new_channel_modal should match snapshot 1`] = `
|
||||
aria-label="Add Channels Dropdown"
|
||||
className="SidebarChannelNavigator__addChannelsCtaLhsButton SidebarChannelNavigator__addChannelsCtaLhsButton--untouched"
|
||||
id="addChannelsCta"
|
||||
onClick={[Function]}
|
||||
>
|
||||
<li
|
||||
aria-label="Add channels"
|
||||
@@ -32,15 +33,35 @@ exports[`components/new_channel_modal should match snapshot 1`] = `
|
||||
id="showNewChannel"
|
||||
onClick={[Function]}
|
||||
show={true}
|
||||
text="Create New Channel"
|
||||
text="Create new channel"
|
||||
/>
|
||||
<MenuItemAction
|
||||
id="showMoreChannels"
|
||||
onClick={[Function]}
|
||||
show={true}
|
||||
text="Browse Channels"
|
||||
text="Browse channels"
|
||||
/>
|
||||
</MenuGroup>
|
||||
</Menu>
|
||||
</MenuWrapper>
|
||||
`;
|
||||
|
||||
exports[`components/new_channel_modal should match snapshot when user has only join channel permissions 1`] = `
|
||||
<button
|
||||
aria-label="Add Channels Dropdown"
|
||||
className="SidebarChannelNavigator__addChannelsCtaLhsButton SidebarChannelNavigator__addChannelsCtaLhsButton--untouched"
|
||||
id="addChannelsCta"
|
||||
onClick={[Function]}
|
||||
>
|
||||
<li
|
||||
aria-label="Add channels"
|
||||
>
|
||||
<i
|
||||
className="icon-plus-box"
|
||||
/>
|
||||
<span>
|
||||
Add Channels
|
||||
</span>
|
||||
</li>
|
||||
</button>
|
||||
`;
|
||||
|
||||
@@ -59,7 +59,7 @@ const AddChannelDropdown = ({
|
||||
id='invitePeople'
|
||||
onClick={invitePeopleModal}
|
||||
icon={<i className='icon-account-plus-outline'/>}
|
||||
text={intl.formatMessage({id: 'sidebar_left.add_channel_dropdown.invitePeople', defaultMessage: 'Invite People'})}
|
||||
text={intl.formatMessage({id: 'sidebar_left.add_channel_dropdown.invitePeople', defaultMessage: 'Invite people'})}
|
||||
extraText={intl.formatMessage({id: 'sidebar_left.add_channel_dropdown.invitePeopleExtraText', defaultMessage: 'Add people to the team'})}
|
||||
/>
|
||||
{showInviteTutorialTip && <InvitePeopleTour/>}
|
||||
@@ -74,7 +74,7 @@ const AddChannelDropdown = ({
|
||||
modalId={ModalIdentifiers.WORK_TEMPLATE}
|
||||
dialogType={WorkTemplateModal}
|
||||
text={intl.formatMessage({id: 'sidebar_left.add_channel_dropdown.work_template', defaultMessage: 'Create from a template'})}
|
||||
extraText={intl.formatMessage({id: 'sidebar_left.add_channel_dropdown.work_template_extra', defaultMessage: 'Set up a channel with linked boards, and playbooks'})}
|
||||
extraText={intl.formatMessage({id: 'sidebar_left.add_channel_dropdown.work_template_extra', defaultMessage: 'Link channels, boards, and playbooks together'})}
|
||||
icon={<i className='icon-layers-outline'/>}
|
||||
className='work-template'
|
||||
/>
|
||||
@@ -88,7 +88,7 @@ const AddChannelDropdown = ({
|
||||
id='showMoreChannels'
|
||||
onClick={showMoreChannelsModal}
|
||||
icon={<i className='icon-globe'/>}
|
||||
text={intl.formatMessage({id: 'sidebar_left.add_channel_dropdown.browseChannels', defaultMessage: 'Browse Channels'})}
|
||||
text={intl.formatMessage({id: 'sidebar_left.add_channel_dropdown.browseChannels', defaultMessage: 'Browse channels'})}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -100,7 +100,7 @@ const AddChannelDropdown = ({
|
||||
id='showNewChannel'
|
||||
onClick={showNewChannelModal}
|
||||
icon={<i className='icon-plus'/>}
|
||||
text={intl.formatMessage({id: 'sidebar_left.add_channel_dropdown.createNewChannel', defaultMessage: 'Create New Channel'})}
|
||||
text={intl.formatMessage({id: 'sidebar_left.add_channel_dropdown.createNewChannel', defaultMessage: 'Create new channel'})}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -113,7 +113,7 @@ const AddChannelDropdown = ({
|
||||
id='createCategory'
|
||||
onClick={showCreateCategoryModal}
|
||||
icon={<i className='icon-folder-plus-outline'/>}
|
||||
text={intl.formatMessage({id: 'sidebar_left.add_channel_dropdown.createCategory', defaultMessage: 'Create New Category'})}
|
||||
text={intl.formatMessage({id: 'sidebar_left.add_channel_dropdown.createCategory', defaultMessage: 'Create new category'})}
|
||||
/>
|
||||
</Menu.Group>);
|
||||
}
|
||||
@@ -143,8 +143,8 @@ const AddChannelDropdown = ({
|
||||
<>
|
||||
<Menu.Group>
|
||||
{workTemplate}
|
||||
{joinPublicChannel}
|
||||
{createChannel}
|
||||
{joinPublicChannel}
|
||||
{createDirectMessage}
|
||||
{showCreateTutorialTip && <CreateAndJoinChannelsTour/>}
|
||||
{createUserGroup}
|
||||
|
||||
@@ -80,6 +80,12 @@ describe('components/new_channel_modal', () => {
|
||||
system_user: {
|
||||
permissions: [Permissions.JOIN_PUBLIC_CHANNELS, Permissions.CREATE_PRIVATE_CHANNEL, Permissions.CREATE_PUBLIC_CHANNEL],
|
||||
},
|
||||
system_user_join_permissions: {
|
||||
permissions: [Permissions.JOIN_PUBLIC_CHANNELS],
|
||||
},
|
||||
system_user_create_public_permissions: {
|
||||
permissions: [Permissions.JOIN_PUBLIC_CHANNELS, Permissions.CREATE_PUBLIC_CHANNEL],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -99,6 +105,25 @@ describe('components/new_channel_modal', () => {
|
||||
).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('should match snapshot when user has only join channel permissions', () => {
|
||||
const userWithJoinChannelsPermission = {
|
||||
currentUserId: 'current_user_id',
|
||||
profiles: {
|
||||
current_user_id: {
|
||||
id: 'current_user_id',
|
||||
roles: 'system_user_join_permissions',
|
||||
},
|
||||
},
|
||||
} as unknown as UsersState;
|
||||
mockState = {...mockState, entities: {...mockState.entities, users: userWithJoinChannelsPermission}};
|
||||
|
||||
expect(
|
||||
shallow(
|
||||
<AddChannelsCtaButton/>,
|
||||
),
|
||||
).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('should find the add channels button when user has permissions', () => {
|
||||
const wrapper = mountWithIntl(
|
||||
<AddChannelsCtaButton/>,
|
||||
@@ -145,4 +170,52 @@ describe('components/new_channel_modal', () => {
|
||||
|
||||
expect(trackEvent).toHaveBeenCalledWith('ui', 'add_channels_cta_button_clicked');
|
||||
});
|
||||
|
||||
test('should not display as a Cta Dropdown when user only has permissions to join channels ', () => {
|
||||
const userWithJoinChannelsPermission = {
|
||||
currentUserId: 'current_user_id',
|
||||
profiles: {
|
||||
current_user_id: {
|
||||
id: 'current_user_id',
|
||||
roles: 'system_user_join_permissions',
|
||||
},
|
||||
},
|
||||
} as unknown as UsersState;
|
||||
mockState = {...mockState, entities: {...mockState.entities, users: userWithJoinChannelsPermission}};
|
||||
|
||||
const wrapper = mountWithIntl(
|
||||
<AddChannelsCtaButton/>,
|
||||
);
|
||||
|
||||
// do not find the menu
|
||||
expect(wrapper.find('.AddChannelsCtaDropdown').exists()).toBeFalsy();
|
||||
|
||||
// only find the button
|
||||
const button = wrapper.find('button#addChannelsCta');
|
||||
expect(button.exists()).toBeTruthy();
|
||||
|
||||
button.simulate('click');
|
||||
|
||||
// when clicked show the browse channels modal
|
||||
expect(trackEvent).toHaveBeenCalledWith('ui', 'browse_channels_button_is_clicked');
|
||||
});
|
||||
|
||||
test('should still display as a Cta Dropdown when user has permissions to create at least one form of channel', () => {
|
||||
const userWithJoinChannelsPermission = {
|
||||
currentUserId: 'current_user_id',
|
||||
profiles: {
|
||||
current_user_id: {
|
||||
id: 'current_user_id',
|
||||
roles: 'system_user_create_public_permissions',
|
||||
},
|
||||
},
|
||||
} as unknown as UsersState;
|
||||
mockState = {...mockState, entities: {...mockState.entities, users: userWithJoinChannelsPermission}};
|
||||
|
||||
const wrapper = mountWithIntl(
|
||||
<AddChannelsCtaButton/>,
|
||||
);
|
||||
|
||||
expect(wrapper.find('.AddChannelsCtaDropdown').exists()).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -80,7 +80,7 @@ const AddChannelsCtaButton = (): JSX.Element | null => {
|
||||
<Menu.ItemAction
|
||||
id='showMoreChannels'
|
||||
onClick={showMoreChannelsModal}
|
||||
text={intl.formatMessage({id: 'sidebar_left.add_channel_dropdown.browseChannels', defaultMessage: 'Browse Channels'})}
|
||||
text={intl.formatMessage({id: 'sidebar_left.add_channel_dropdown.browseChannels', defaultMessage: 'Browse channels'})}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -91,7 +91,7 @@ const AddChannelsCtaButton = (): JSX.Element | null => {
|
||||
<Menu.ItemAction
|
||||
id='showNewChannel'
|
||||
onClick={showNewChannelModal}
|
||||
text={intl.formatMessage({id: 'sidebar_left.add_channel_dropdown.createNewChannel', defaultMessage: 'Create New Channel'})}
|
||||
text={intl.formatMessage({id: 'sidebar_left.add_channel_dropdown.createNewChannel', defaultMessage: 'Create new channel'})}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -106,8 +106,28 @@ const AddChannelsCtaButton = (): JSX.Element | null => {
|
||||
);
|
||||
};
|
||||
|
||||
const trackOpen = (opened: boolean) => {
|
||||
openAddChannelsCtaOpen(opened);
|
||||
const addChannelsButton = (btnCallback?: () => void) => {
|
||||
const handleClick = () => btnCallback?.();
|
||||
return (
|
||||
<button
|
||||
className={buttonClass}
|
||||
id={'addChannelsCta'}
|
||||
aria-label={intl.formatMessage({id: 'sidebar_left.add_channel_dropdown.dropdownAriaLabel', defaultMessage: 'Add Channels Dropdown'})}
|
||||
onClick={handleClick}
|
||||
>
|
||||
<li
|
||||
aria-label={intl.formatMessage({id: 'sidebar_left.sidebar_channel_navigator.addChannelsCta', defaultMessage: 'Add channels'})}
|
||||
>
|
||||
<i className='icon-plus-box'/>
|
||||
<span>
|
||||
{intl.formatMessage({id: 'sidebar_left.addChannelsCta', defaultMessage: 'Add Channels'})}
|
||||
</span>
|
||||
</li>
|
||||
</button>
|
||||
);
|
||||
};
|
||||
|
||||
const storePreferencesAndTrackEvent = () => {
|
||||
trackEvent('ui', 'add_channels_cta_button_clicked');
|
||||
if (!touchedAddChannelsCtaButton) {
|
||||
dispatch(savePreferences(
|
||||
@@ -122,26 +142,26 @@ const AddChannelsCtaButton = (): JSX.Element | null => {
|
||||
}
|
||||
};
|
||||
|
||||
const trackOpen = (opened: boolean) => {
|
||||
openAddChannelsCtaOpen(opened);
|
||||
storePreferencesAndTrackEvent();
|
||||
};
|
||||
|
||||
if (!canCreateChannel) {
|
||||
const browseChannelsAction = () => {
|
||||
showMoreChannelsModal();
|
||||
storePreferencesAndTrackEvent();
|
||||
};
|
||||
return addChannelsButton(browseChannelsAction);
|
||||
}
|
||||
|
||||
return (
|
||||
<MenuWrapper
|
||||
className='AddChannelsCtaDropdown'
|
||||
onToggle={trackOpen}
|
||||
open={isAddChannelCtaOpen}
|
||||
>
|
||||
<button
|
||||
className={buttonClass}
|
||||
id={'addChannelsCta'}
|
||||
aria-label={intl.formatMessage({id: 'sidebar_left.add_channel_dropdown.dropdownAriaLabel', defaultMessage: 'Add Channels Dropdown'})}
|
||||
>
|
||||
<li
|
||||
aria-label={intl.formatMessage({id: 'sidebar_left.sidebar_channel_navigator.addChannelsCta', defaultMessage: 'Add channels'})}
|
||||
>
|
||||
<i className='icon-plus-box'/>
|
||||
<span>
|
||||
{intl.formatMessage({id: 'sidebar_left.addChannelsCta', defaultMessage: 'Add Channels'})}
|
||||
</span>
|
||||
</li>
|
||||
</button>
|
||||
{addChannelsButton()}
|
||||
<Menu
|
||||
id='AddChannelCtaDropdown'
|
||||
ariaLabel={intl.formatMessage({id: 'sidebar_left.add_channel_cta_dropdown.dropdownAriaLabel', defaultMessage: 'Add Channels Dropdown'})}
|
||||
|
||||
@@ -93,6 +93,28 @@ exports[`components/signup/Signup should match snapshot for all signup options e
|
||||
onChange={[Function]}
|
||||
value=""
|
||||
/>
|
||||
<div
|
||||
className="newsletter"
|
||||
>
|
||||
<span
|
||||
className="interested"
|
||||
>
|
||||
Interested in receiving Mattermost security updates via newsletter?
|
||||
</span>
|
||||
<span
|
||||
className="link"
|
||||
>
|
||||
Sign up at
|
||||
<ExternalLink
|
||||
href="https://mattermost.com/security-updates/"
|
||||
key=".1"
|
||||
location="signup"
|
||||
>
|
||||
https://mattermost.com/security-updates/
|
||||
</ExternalLink>
|
||||
.
|
||||
</span>
|
||||
</div>
|
||||
<SaveButton
|
||||
btnClass="btn-primary"
|
||||
defaultMessage="Create account"
|
||||
@@ -244,6 +266,28 @@ exports[`components/signup/Signup should match snapshot for all signup options e
|
||||
onChange={[Function]}
|
||||
value=""
|
||||
/>
|
||||
<div
|
||||
className="newsletter"
|
||||
>
|
||||
<span
|
||||
className="interested"
|
||||
>
|
||||
Interested in receiving Mattermost security updates via newsletter?
|
||||
</span>
|
||||
<span
|
||||
className="link"
|
||||
>
|
||||
Sign up at
|
||||
<ExternalLink
|
||||
href="https://mattermost.com/security-updates/"
|
||||
key=".1"
|
||||
location="signup"
|
||||
>
|
||||
https://mattermost.com/security-updates/
|
||||
</ExternalLink>
|
||||
.
|
||||
</span>
|
||||
</div>
|
||||
<SaveButton
|
||||
btnClass="btn-primary"
|
||||
defaultMessage="Create account"
|
||||
|
||||
@@ -166,6 +166,22 @@
|
||||
margin-top: 22px;
|
||||
}
|
||||
|
||||
.newsletter {
|
||||
margin-top: 24px;
|
||||
margin-bottom: 32px;
|
||||
color: var(--center-channel-color-rgb);
|
||||
font-family: 'Open Sans';
|
||||
font-size: 12px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 16px;
|
||||
|
||||
.interested {
|
||||
display: block;
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
|
||||
.signup-body-card-form-button-submit {
|
||||
@include primary-button;
|
||||
@include button-large;
|
||||
|
||||
@@ -5,7 +5,7 @@ import React from 'react';
|
||||
import {shallow, ReactWrapper} from 'enzyme';
|
||||
import {IntlProvider} from 'react-intl';
|
||||
import {BrowserRouter} from 'react-router-dom';
|
||||
import {act} from '@testing-library/react';
|
||||
import {act, screen} from '@testing-library/react';
|
||||
|
||||
import * as global_actions from 'actions/global_actions';
|
||||
|
||||
@@ -15,16 +15,18 @@ import Signup from 'components/signup/signup';
|
||||
import Input from 'components/widgets/inputs/input/input';
|
||||
import PasswordInput from 'components/widgets/inputs/password_input/password_input';
|
||||
import SaveButton from 'components/save_button';
|
||||
import * as useCWSAvailabilityCheckAll from 'components/common/hooks/useCWSAvailabilityCheck';
|
||||
|
||||
import {RequestStatus} from 'mattermost-redux/constants';
|
||||
import {ClientConfig} from '@mattermost/types/config';
|
||||
import {GlobalState} from 'types/store';
|
||||
import {WindowSizes} from 'utils/constants';
|
||||
import {renderWithIntlAndStore} from 'tests/react_testing_utils';
|
||||
|
||||
let mockState: GlobalState;
|
||||
let mockLocation = {pathname: '', search: '', hash: ''};
|
||||
const mockHistoryPush = jest.fn();
|
||||
let mockLicense = {IsLicensed: 'true'};
|
||||
let mockLicense = {IsLicensed: 'true', Cloud: 'false'};
|
||||
let mockConfig: Partial<ClientConfig>;
|
||||
let mockDispatch = jest.fn();
|
||||
|
||||
@@ -96,7 +98,7 @@ describe('components/signup/Signup', () => {
|
||||
beforeEach(() => {
|
||||
mockLocation = {pathname: '', search: '', hash: ''};
|
||||
|
||||
mockLicense = {IsLicensed: 'true'};
|
||||
mockLicense = {IsLicensed: 'true', Cloud: 'false'};
|
||||
|
||||
mockState = {
|
||||
entities: {
|
||||
@@ -178,7 +180,7 @@ describe('components/signup/Signup', () => {
|
||||
});
|
||||
|
||||
it('should match snapshot for all signup options enabled with isLicensed disabled', () => {
|
||||
mockLicense = {IsLicensed: 'false'};
|
||||
mockLicense = {IsLicensed: 'false', Cloud: 'false'};
|
||||
|
||||
const wrapper = shallow(
|
||||
<Signup/>,
|
||||
@@ -295,4 +297,45 @@ describe('components/signup/Signup', () => {
|
||||
expect(wrapper.find('.content-layout-column-title').text()).toEqual('This invite link is invalid');
|
||||
});
|
||||
});
|
||||
|
||||
it('should show newsletter check box opt-in for self-hosted non airgapped workspaces', async () => {
|
||||
jest.spyOn(useCWSAvailabilityCheckAll, 'default').mockImplementation(() => true);
|
||||
mockLicense = {IsLicensed: 'true', Cloud: 'false'};
|
||||
|
||||
const {container: signupContainer} = renderWithIntlAndStore(
|
||||
<BrowserRouter>
|
||||
<Signup/>
|
||||
</BrowserRouter>, {});
|
||||
|
||||
screen.getByTestId('signup-body-card-form-check-newsletter');
|
||||
const checkInput = screen.getByTestId('signup-body-card-form-check-newsletter');
|
||||
expect(checkInput).toHaveAttribute('type', 'checkbox');
|
||||
|
||||
expect(signupContainer).toHaveTextContent(/I would like to receive Mattermost security updates via newsletter. Data Terms and Conditions apply/);
|
||||
});
|
||||
|
||||
it('should NOT show newsletter check box opt-in for self-hosted AND airgapped workspaces', async () => {
|
||||
jest.spyOn(useCWSAvailabilityCheckAll, 'default').mockImplementation(() => false);
|
||||
mockLicense = {IsLicensed: 'true', Cloud: 'false'};
|
||||
|
||||
const {container: signupContainer} = renderWithIntlAndStore(
|
||||
<BrowserRouter>
|
||||
<Signup/>
|
||||
</BrowserRouter>, {});
|
||||
|
||||
expect(() => screen.getByTestId('signup-body-card-form-check-newsletter')).toThrow();
|
||||
expect(signupContainer).toHaveTextContent('Interested in receiving Mattermost security updates via newsletter?Sign up at https://mattermost.com/security-updates/.');
|
||||
});
|
||||
|
||||
it('should not show any newsletter related opt-in or text for cloud', async () => {
|
||||
jest.spyOn(useCWSAvailabilityCheckAll, 'default').mockImplementation(() => true);
|
||||
mockLicense = {IsLicensed: 'true', Cloud: 'true'};
|
||||
|
||||
renderWithIntlAndStore(
|
||||
<BrowserRouter>
|
||||
<Signup/>
|
||||
</BrowserRouter>, {});
|
||||
|
||||
expect(() => screen.getByTestId('signup-body-card-form-check-newsletter')).toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -48,9 +48,12 @@ import LoginOpenIDIcon from 'components/widgets/icons/login_openid_icon';
|
||||
import LoginOffice365Icon from 'components/widgets/icons/login_office_365_icon';
|
||||
import Input, {CustomMessageInputType, SIZE} from 'components/widgets/inputs/input/input';
|
||||
import PasswordInput from 'components/widgets/inputs/password_input/password_input';
|
||||
import CheckInput from 'components/widgets/inputs/check';
|
||||
import SaveButton from 'components/save_button';
|
||||
import useCWSAvailabilityCheck from 'components/common/hooks/useCWSAvailabilityCheck';
|
||||
import ExternalLink from 'components/external_link';
|
||||
|
||||
import {Constants, ItemStatus, ValidationErrors} from 'utils/constants';
|
||||
import {Constants, HostedCustomerLinks, ItemStatus, ValidationErrors} from 'utils/constants';
|
||||
import {isValidUsername, isValidPassword, getPasswordConfig, getRoleFromTrackFlow, getMediumFromTrackFlow} from 'utils/utils';
|
||||
|
||||
import './signup.scss';
|
||||
@@ -99,7 +102,7 @@ const Signup = ({onCustomizeHeader}: SignupProps) => {
|
||||
TermsOfServiceLink,
|
||||
PrivacyPolicyLink,
|
||||
} = config;
|
||||
const {IsLicensed} = useSelector(getLicense);
|
||||
const {IsLicensed, Cloud} = useSelector(getLicense);
|
||||
const loggedIn = Boolean(useSelector(getCurrentUserId));
|
||||
const useCaseOnboarding = useSelector(getUseCaseOnboarding);
|
||||
const usedBefore = useSelector((state: GlobalState) => (!inviteId && !loggedIn && token ? getGlobalItem(state, token, null) : undefined));
|
||||
@@ -110,6 +113,7 @@ const Signup = ({onCustomizeHeader}: SignupProps) => {
|
||||
const passwordInput = useRef<HTMLInputElement>(null);
|
||||
|
||||
const isLicensed = IsLicensed === 'true';
|
||||
const isCloud = Cloud === 'true';
|
||||
const enableOpenServer = EnableOpenServer === 'true';
|
||||
const noAccounts = NoAccounts === 'true';
|
||||
const enableSignUpWithEmail = EnableSignUpWithEmail === 'true';
|
||||
@@ -136,12 +140,24 @@ const Signup = ({onCustomizeHeader}: SignupProps) => {
|
||||
const [teamName, setTeamName] = useState(parsedTeamName ?? '');
|
||||
const [alertBanner, setAlertBanner] = useState<AlertBannerProps | null>(null);
|
||||
const [isMobileView, setIsMobileView] = useState(false);
|
||||
const [subscribeToSecurityNewsletter, setSubscribeToSecurityNewsletter] = useState(false);
|
||||
|
||||
const canReachCWS = useCWSAvailabilityCheck();
|
||||
|
||||
const enableExternalSignup = enableSignUpWithGitLab || enableSignUpWithOffice365 || enableSignUpWithGoogle || enableSignUpWithOpenId || enableLDAP || enableSAML;
|
||||
const hasError = Boolean(emailError || nameError || passwordError || serverError || alertBanner);
|
||||
const canSubmit = Boolean(email && name && password) && !hasError && !loading;
|
||||
const {error: passwordInfo} = isValidPassword('', getPasswordConfig(config), intl);
|
||||
|
||||
const subscribeToSecurityNewsletterFunc = () => {
|
||||
try {
|
||||
Client4.subscribeToNewsletter({email, subscribed_content: 'security_newsletter'});
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(error);
|
||||
}
|
||||
};
|
||||
|
||||
const getExternalSignupOptions = () => {
|
||||
const externalLoginOptions: ExternalLoginButtonType[] = [];
|
||||
|
||||
@@ -564,6 +580,9 @@ const Signup = ({onCustomizeHeader}: SignupProps) => {
|
||||
}
|
||||
|
||||
await handleSignupSuccess(user, data as UserProfile);
|
||||
if (subscribeToSecurityNewsletter) {
|
||||
subscribeToSecurityNewsletterFunc();
|
||||
}
|
||||
} else {
|
||||
setIsWaiting(false);
|
||||
}
|
||||
@@ -571,6 +590,60 @@ const Signup = ({onCustomizeHeader}: SignupProps) => {
|
||||
|
||||
const handleReturnButtonOnClick = () => history.replace('/');
|
||||
|
||||
const getNewsletterCheck = () => {
|
||||
if (isCloud) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (canReachCWS) {
|
||||
return (
|
||||
<CheckInput
|
||||
id='signup-body-card-form-check-newsletter'
|
||||
name='newsletter'
|
||||
onChange={() => setSubscribeToSecurityNewsletter(!subscribeToSecurityNewsletter)}
|
||||
text={
|
||||
formatMessage(
|
||||
{id: 'newsletter_optin.checkmark.text', defaultMessage: 'I would like to receive Mattermost security updates via newsletter. Data <a>Terms and Conditions</a> apply'},
|
||||
{
|
||||
a: (chunks: React.ReactNode | React.ReactNodeArray) => (
|
||||
<ExternalLink
|
||||
location='signup-newsletter-checkmark'
|
||||
href={HostedCustomerLinks.SECURITY_UPDATES}
|
||||
>
|
||||
{chunks}
|
||||
</ExternalLink>
|
||||
),
|
||||
},
|
||||
)}
|
||||
checked={subscribeToSecurityNewsletter}
|
||||
/>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<div className='newsletter'>
|
||||
<span className='interested'>
|
||||
{formatMessage({id: 'newsletter_optin.title', defaultMessage: 'Interested in receiving Mattermost security updates via newsletter?'})}
|
||||
</span>
|
||||
<span className='link'>
|
||||
{formatMessage(
|
||||
{id: 'newsletter_optin.desc', defaultMessage: 'Sign up at <a>{link}</a>.'},
|
||||
{
|
||||
link: HostedCustomerLinks.SECURITY_UPDATES,
|
||||
a: (chunks: React.ReactNode | React.ReactNodeArray) => (
|
||||
<ExternalLink
|
||||
location='signup'
|
||||
href={HostedCustomerLinks.SECURITY_UPDATES}
|
||||
>
|
||||
{chunks}
|
||||
</ExternalLink>
|
||||
),
|
||||
},
|
||||
)}
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const handleOnBlur = (e: FocusEvent<HTMLInputElement>, inputId: string) => {
|
||||
const text = e.target.value;
|
||||
if (!text) {
|
||||
@@ -736,6 +809,7 @@ const Signup = ({onCustomizeHeader}: SignupProps) => {
|
||||
error={passwordError}
|
||||
onBlur={(e) => handleOnBlur(e, 'password')}
|
||||
/>
|
||||
{getNewsletterCheck()}
|
||||
<SaveButton
|
||||
extraClasses='signup-body-card-form-button-submit large'
|
||||
saving={isWaiting}
|
||||
|
||||
@@ -33,6 +33,7 @@ export type ChannelsTourTipProps = {
|
||||
hideBackdrop?: boolean;
|
||||
tippyBlueStyle?: boolean;
|
||||
showOptOut?: boolean;
|
||||
interactivePunchOut?: boolean;
|
||||
}
|
||||
|
||||
export const ChannelsTourTip = ({
|
||||
@@ -50,6 +51,7 @@ export const ChannelsTourTip = ({
|
||||
hideBackdrop = false,
|
||||
tippyBlueStyle = false,
|
||||
showOptOut = true,
|
||||
interactivePunchOut = false,
|
||||
}: ChannelsTourTipProps) => {
|
||||
const {
|
||||
show,
|
||||
@@ -131,6 +133,7 @@ export const ChannelsTourTip = ({
|
||||
hideBackdrop={hideBackdrop}
|
||||
tippyBlueStyle={tippyBlueStyle}
|
||||
showOptOut={showOptOut}
|
||||
interactivePunchOut={interactivePunchOut}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -9,7 +9,7 @@ import {Constants, Preferences} from 'utils/constants';
|
||||
import {getCurrentUserId} from 'mattermost-redux/selectors/entities/common';
|
||||
import {savePreferences} from 'mattermost-redux/actions/preferences';
|
||||
|
||||
import {TourTip, useMeasurePunchouts} from '@mattermost/components';
|
||||
import {TourTip, useFollowElementDimensions, useMeasurePunchouts} from '@mattermost/components';
|
||||
|
||||
const translate = {x: 2, y: 25};
|
||||
|
||||
@@ -17,6 +17,9 @@ const CRTThreadsPaneTutorialTip = () => {
|
||||
const dispatch = useDispatch();
|
||||
const {formatMessage} = useIntl();
|
||||
const currentUserId = useSelector(getCurrentUserId);
|
||||
|
||||
const dimensions = useFollowElementDimensions('sidebar-right');
|
||||
|
||||
const title = (
|
||||
<FormattedMessage
|
||||
id='tutorial_threads.threads_pane.title'
|
||||
@@ -60,7 +63,7 @@ const CRTThreadsPaneTutorialTip = () => {
|
||||
dispatch(savePreferences(currentUserId, preferences));
|
||||
};
|
||||
|
||||
const overlayPunchOut = useMeasurePunchouts(['rhsContainer'], []);
|
||||
const overlayPunchOut = useMeasurePunchouts(['rhsContainer'], [dimensions?.width]);
|
||||
|
||||
return (
|
||||
<TourTip
|
||||
|
||||
@@ -8,7 +8,7 @@ import {useMeasurePunchouts} from '@mattermost/components';
|
||||
|
||||
import OnboardingTourTip from './onboarding_tour_tip';
|
||||
|
||||
const translate = {x: 0, y: 18};
|
||||
const translate = {x: 0, y: 70};
|
||||
|
||||
export const CreateAndJoinChannelsTour = () => {
|
||||
const title = (
|
||||
@@ -26,7 +26,7 @@ export const CreateAndJoinChannelsTour = () => {
|
||||
</p>
|
||||
);
|
||||
|
||||
const overlayPunchOut = useMeasurePunchouts(['showMoreChannels', 'invitePeople'], [], {y: -8, height: 16, x: 0, width: 0});
|
||||
const overlayPunchOut = useMeasurePunchouts(['showMoreChannels', 'showNewChannel'], [], {y: -8, height: 16, x: 0, width: 0});
|
||||
|
||||
return (
|
||||
<OnboardingTourTip
|
||||
|
||||
@@ -26,7 +26,7 @@ export const InvitePeopleTour = () => {
|
||||
</p>
|
||||
);
|
||||
|
||||
const overlayPunchOut = useMeasurePunchouts(['showMoreChannels', 'invitePeople'], [], {y: -8, height: 16, x: 0, width: 0});
|
||||
const overlayPunchOut = useMeasurePunchouts(['invitePeople'], [], {y: -8, height: 16, x: 0, width: 0});
|
||||
|
||||
return (
|
||||
<OnboardingTourTip
|
||||
|
||||
@@ -4,16 +4,16 @@
|
||||
import React from 'react';
|
||||
import {FormattedMessage, useIntl} from 'react-intl';
|
||||
|
||||
import {useMeasurePunchouts} from '@mattermost/components';
|
||||
import {useFollowElementDimensions, useMeasurePunchouts} from '@mattermost/components';
|
||||
|
||||
import OnboardingWorkTemplateTourTip from './worktemplate_explore_tour_tip';
|
||||
import {useShowTourTip} from './useShowTourTip';
|
||||
|
||||
export const BoardsTourTip = (): JSX.Element | null => {
|
||||
const {formatMessage} = useIntl();
|
||||
|
||||
const {playbooksCount, boardsCount, showBoardsTour} = useShowTourTip();
|
||||
const overlayPunchOut = useMeasurePunchouts(['sidebar-right'], []);
|
||||
const dimensions = useFollowElementDimensions('sidebar-right');
|
||||
const overlayPunchOut = useMeasurePunchouts(['sidebar-right'], [dimensions?.width]);
|
||||
|
||||
if (!showBoardsTour) {
|
||||
return null;
|
||||
@@ -60,6 +60,7 @@ export const BoardsTourTip = (): JSX.Element | null => {
|
||||
singleTip={playbooksCount === 0}
|
||||
placement='left-start'
|
||||
showOptOut={false}
|
||||
interactivePunchOut={true}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
import React from 'react';
|
||||
import {FormattedMessage, useIntl} from 'react-intl';
|
||||
|
||||
import {useMeasurePunchouts} from '@mattermost/components';
|
||||
import {useFollowElementDimensions, useMeasurePunchouts} from '@mattermost/components';
|
||||
|
||||
import {useShowTourTip} from './useShowTourTip';
|
||||
import OnboardingWorkTemplateTourTip from './worktemplate_explore_tour_tip';
|
||||
@@ -12,7 +12,8 @@ import OnboardingWorkTemplateTourTip from './worktemplate_explore_tour_tip';
|
||||
export const PlaybooksTourTip = (): JSX.Element | null => {
|
||||
const {formatMessage} = useIntl();
|
||||
const {playbooksCount, boardsCount, showPlaybooksTour} = useShowTourTip();
|
||||
const overlayPunchOut = useMeasurePunchouts(['sidebar-right'], []);
|
||||
const dimensions = useFollowElementDimensions('sidebar-right');
|
||||
const overlayPunchOut = useMeasurePunchouts(['sidebar-right'], [dimensions?.width]);
|
||||
|
||||
if (!showPlaybooksTour) {
|
||||
return null;
|
||||
@@ -59,6 +60,7 @@ export const PlaybooksTourTip = (): JSX.Element | null => {
|
||||
overlayPunchOut={overlayPunchOut}
|
||||
placement='left-start'
|
||||
showOptOut={false}
|
||||
interactivePunchOut={true}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
19
webapp/channels/src/components/widgets/inputs/check/check.scss
Обычный файл
@@ -0,0 +1,19 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
.check-input {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
margin-top: 24px;
|
||||
margin-bottom: 32px;
|
||||
|
||||
.text {
|
||||
margin-left: 8px;
|
||||
color: var(--center-channel-color-rgb);
|
||||
font-family: 'Open Sans';
|
||||
font-size: 12px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 16px;
|
||||
}
|
||||
}
|
||||
28
webapp/channels/src/components/widgets/inputs/check/index.tsx
Обычный файл
@@ -0,0 +1,28 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import React, {ReactNode} from 'react';
|
||||
import './check.scss';
|
||||
|
||||
type Props = {
|
||||
id: string;
|
||||
name: string;
|
||||
text: ReactNode;
|
||||
onChange: () => void;
|
||||
checked: boolean;
|
||||
}
|
||||
|
||||
function CheckInput(props: Props) {
|
||||
return (
|
||||
<div className='check-input'>
|
||||
<input
|
||||
{...props}
|
||||
data-testid={props.id}
|
||||
type='checkbox'
|
||||
/>
|
||||
<span className='text'>{props.text}</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default CheckInput;
|
||||
@@ -0,0 +1,26 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`components/widgets/inputs/Input should match snapshot 1`] = `
|
||||
<div
|
||||
className="Input_container"
|
||||
>
|
||||
<fieldset
|
||||
className="Input_fieldset"
|
||||
>
|
||||
<legend
|
||||
className="Input_legend"
|
||||
/>
|
||||
<div
|
||||
className="Input_wrapper"
|
||||
>
|
||||
<input
|
||||
className="Input form-control medium"
|
||||
id="input_"
|
||||
onBlur={[Function]}
|
||||
onChange={[Function]}
|
||||
onFocus={[Function]}
|
||||
/>
|
||||
</div>
|
||||
</fieldset>
|
||||
</div>
|
||||
`;
|
||||
@@ -49,11 +49,16 @@
|
||||
.Input_wrapper {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
align-items: center;
|
||||
padding: 0 16px;
|
||||
margin: 2px 0;
|
||||
color: rgba(var(--center-channel-color-rgb), 0.56);
|
||||
font-size: 14px;
|
||||
line-height: 20px;
|
||||
|
||||
> :not(:first-child) {
|
||||
margin-left: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.Input_limit-exceeded {
|
||||
@@ -196,4 +201,10 @@
|
||||
background-color: rgba(var(--center-channel-color-rgb), 0.04);
|
||||
}
|
||||
}
|
||||
|
||||
.Input__clear {
|
||||
display: flex;
|
||||
color: rgba(var(--center-channel-color), 0.68);
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {mount, shallow} from 'enzyme';
|
||||
import React from 'react';
|
||||
|
||||
import OverlayTrigger from 'components/overlay_trigger';
|
||||
|
||||
import Input from './input';
|
||||
|
||||
describe('components/widgets/inputs/Input', () => {
|
||||
test('should match snapshot', () => {
|
||||
const wrapper = shallow(
|
||||
<Input/>,
|
||||
);
|
||||
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('should render with clearable enabled', () => {
|
||||
const value = 'value';
|
||||
const clearableTooltipText = 'tooltip text';
|
||||
const onClear = jest.fn();
|
||||
|
||||
const wrapper = shallow(
|
||||
<Input
|
||||
value={value}
|
||||
clearable={true}
|
||||
clearableTooltipText={clearableTooltipText}
|
||||
onClear={onClear}
|
||||
/>,
|
||||
);
|
||||
|
||||
const clear = wrapper.find('.Input__clear');
|
||||
expect(clear.length).toEqual(1);
|
||||
expect(wrapper.find('CloseCircleIcon').length).toEqual(1);
|
||||
|
||||
const tooltip = wrapper.find(OverlayTrigger);
|
||||
expect(tooltip.length).toEqual(1);
|
||||
|
||||
const overlay = mount(tooltip.prop('overlay'));
|
||||
expect(overlay.text()).toEqual(clearableTooltipText);
|
||||
|
||||
clear.first().simulate('mousedown');
|
||||
|
||||
expect(onClear).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
@@ -3,10 +3,14 @@
|
||||
|
||||
import React, {useState, useEffect} from 'react';
|
||||
import {useIntl} from 'react-intl';
|
||||
|
||||
import classNames from 'classnames';
|
||||
|
||||
import {ItemStatus} from 'utils/constants';
|
||||
import {CloseCircleIcon} from '@mattermost/compass-icons/components';
|
||||
|
||||
import OverlayTrigger from 'components/overlay_trigger';
|
||||
import Tooltip from 'components/tooltip';
|
||||
|
||||
import Constants, {ItemStatus} from 'utils/constants';
|
||||
|
||||
import './input.scss';
|
||||
|
||||
@@ -32,6 +36,9 @@ interface InputProps extends React.InputHTMLAttributes<HTMLInputElement> {
|
||||
useLegend?: boolean;
|
||||
customMessage?: CustomMessageInputType;
|
||||
inputSize?: SIZE;
|
||||
clearable?: boolean;
|
||||
clearableTooltipText?: string;
|
||||
onClear?: () => void;
|
||||
}
|
||||
|
||||
const Input = React.forwardRef((
|
||||
@@ -56,9 +63,12 @@ const Input = React.forwardRef((
|
||||
maxLength,
|
||||
inputSize = SIZE.MEDIUM,
|
||||
disabled,
|
||||
clearable,
|
||||
clearableTooltipText,
|
||||
onFocus,
|
||||
onBlur,
|
||||
onChange,
|
||||
onClear,
|
||||
...otherProps
|
||||
}: InputProps,
|
||||
ref?: React.Ref<HTMLInputElement>,
|
||||
@@ -109,6 +119,12 @@ const Input = React.forwardRef((
|
||||
}
|
||||
};
|
||||
|
||||
const handleOnClear = () => {
|
||||
if (onClear) {
|
||||
onClear();
|
||||
}
|
||||
};
|
||||
|
||||
const validateInput = () => {
|
||||
if (!required || (value !== null && value !== '')) {
|
||||
return;
|
||||
@@ -121,6 +137,26 @@ const Input = React.forwardRef((
|
||||
const error = customInputLabel?.type === 'error';
|
||||
const limitExceeded = limit && value && !Array.isArray(value) ? value.toString().length - limit : 0;
|
||||
|
||||
const clearButton = value && clearable ? (
|
||||
<div
|
||||
className='Input__clear'
|
||||
onMouseDown={handleOnClear}
|
||||
onTouchEnd={handleOnClear}
|
||||
>
|
||||
<OverlayTrigger
|
||||
delayShow={Constants.OVERLAY_TIME_DELAY}
|
||||
placement='bottom'
|
||||
overlay={(
|
||||
<Tooltip id={'InputClearTooltip'}>
|
||||
{clearableTooltipText || formatMessage({id: 'widget.input.clear', defaultMessage: 'Clear'})}
|
||||
</Tooltip>
|
||||
)}
|
||||
>
|
||||
<CloseCircleIcon size={18}/>
|
||||
</OverlayTrigger>
|
||||
</div>
|
||||
) : null;
|
||||
|
||||
return (
|
||||
<div className={classNames('Input_container', containerClassName, {disabled})}>
|
||||
<fieldset
|
||||
@@ -157,6 +193,7 @@ const Input = React.forwardRef((
|
||||
</span>
|
||||
)}
|
||||
{inputSuffix}
|
||||
{clearButton}
|
||||
</div>
|
||||
{addon}
|
||||
</fieldset>
|
||||
|
||||
@@ -31,6 +31,9 @@ type Props = {
|
||||
listId?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use the "webapp/channels/src/components/menu" instead.
|
||||
*/
|
||||
export default class Menu extends React.PureComponent<Props> {
|
||||
public static Header = MenuHeader
|
||||
public static Group = MenuGroup
|
||||
|
||||
@@ -10,6 +10,9 @@ type Props = {
|
||||
children?: React.ReactNode;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use the "webapp/channels/src/components/menu" instead.
|
||||
*/
|
||||
export default class MenuGroup extends React.PureComponent<Props> {
|
||||
handleDividerClick = (e: React.MouseEvent): void => {
|
||||
e.preventDefault();
|
||||
|
||||
@@ -11,6 +11,9 @@ type Props = {
|
||||
onClick?: () => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use the "webapp/channels/src/components/menu" instead.
|
||||
*/
|
||||
export default class MenuHeader extends React.PureComponent<Props> {
|
||||
public render() {
|
||||
const {children, onClick} = this.props;
|
||||
|
||||
@@ -205,7 +205,6 @@ describe('components/widgets/menu/menu_items/menu_cloud_trial', () => {
|
||||
};
|
||||
const store = mockStore(state);
|
||||
const wrapper = mountWithIntl(<Provider store={store}><MenuCloudTrial id='menuCloudTrial'/></Provider>);
|
||||
console.log(wrapper.debug());
|
||||
expect(wrapper.find('.open-learn-more-trial-modal').exists()).toEqual(true);
|
||||
});
|
||||
|
||||
|
||||
@@ -6,6 +6,9 @@ import classNames from 'classnames';
|
||||
|
||||
import './menu_item.scss';
|
||||
|
||||
/**
|
||||
* @deprecated Use the "webapp/channels/src/components/menu" instead.
|
||||
*/
|
||||
export default function menuItem(Component: React.ComponentType<any>) {
|
||||
type Props = {
|
||||
show: boolean;
|
||||
|
||||
@@ -60,6 +60,9 @@ type State = {
|
||||
show: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use the "webapp/channels/src/components/menu" instead.
|
||||
*/
|
||||
export default class SubMenuItem extends React.PureComponent<Props, State> {
|
||||
private node: React.RefObject<HTMLLIElement>;
|
||||
|
||||
|
||||
@@ -29,6 +29,9 @@ type State = {
|
||||
open: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use the "webapp/channels/src/components/menu" instead.
|
||||
*/
|
||||
export default class MenuWrapper extends React.PureComponent<Props, State> {
|
||||
private node: React.RefObject<HTMLDivElement>;
|
||||
|
||||
|
||||
@@ -13,6 +13,9 @@ type Props = {
|
||||
show: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use the "webapp/channels/src/components/menu" instead.
|
||||
*/
|
||||
export default function MenuWrapperAnimation(props: Props) {
|
||||
if (isMobile()) {
|
||||
if (props.show) {
|
||||
|
||||
@@ -298,7 +298,7 @@ const WorkTemplateModal = () => {
|
||||
let confirmButtonAction;
|
||||
switch (modalState) {
|
||||
case ModalState.Menu:
|
||||
title = formatMessage({id: 'work_templates.menu.modal_title', defaultMessage: 'Start from a template'});
|
||||
title = formatMessage({id: 'work_templates.menu.modal_title', defaultMessage: 'Create from a template'});
|
||||
break;
|
||||
case ModalState.Preview:
|
||||
title = formatMessage({id: 'work_templates.preview.modal_title', defaultMessage: 'Preview {useCase}'}, {useCase: selectedTemplate?.useCase});
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
"about.enterpriseEditione1": "Корпоративна версия",
|
||||
"about.hash": "Хеш на компилация:",
|
||||
"about.hashee": "Хеш на EE компилация:",
|
||||
"about.hashwebapp": "Webapp билд хеш:",
|
||||
"about.licensed": "Лицензиран на:",
|
||||
"about.notice": "Използването на Mattermost е възможно благодарение на софтуер с отворен код използван в нашите <linkServer>сървърно</linkServer>, <linkDesktop>настолно</linkDesktop> и <linkMobile>мобилно</linkMobile> приложения.",
|
||||
"about.privacy": "Политика за поверителност",
|
||||
@@ -2363,7 +2362,6 @@
|
||||
"app.channel.post_update_channel_purpose_message.removed": "{username} премахна целта на канала (беше: {old})",
|
||||
"app.channel.post_update_channel_purpose_message.updated_from": "{username} актуализира целта на канала от: {old} до: {new}",
|
||||
"app.channel.post_update_channel_purpose_message.updated_to": "{username} актуализира целта на канала до: {new}",
|
||||
"app.plugin.marketplace_plugins.app_error": "Грешка при свързване със сървъра на пазара. Моля, проверете настройките си в [Системна конзола]({siteURL}/admin_console/plugins/plugin_management).",
|
||||
"apps.error": "Грешка: {error}",
|
||||
"apps.error.command.field_missing": "Липсват задължителни полета: `{fieldName}`.",
|
||||
"apps.error.command.same_channel": "Повтаря се каналът с поле `{fieldName}`: `{option}`.",
|
||||
@@ -3415,7 +3413,6 @@
|
||||
"login_mfa.token": "МФУ маркер",
|
||||
"manage_channel_groups_modal.search_placeholder": "Търсене на групи",
|
||||
"manage_team_groups_modal.search_placeholder": "Търсене на групи",
|
||||
"marketplace_list.count_total_page": "{startCount, number} - {endCount, number} {total, plural, one {приставка} other {приставки}} от {total, number} общо",
|
||||
"marketplace_modal.install_plugins": "Инсталирай приставки",
|
||||
"marketplace_modal.installing": "Инсталиране ...",
|
||||
"marketplace_modal.list.configure": "Конфигуриране",
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
"about.enterpriseEditione1": "Enterprise Edition",
|
||||
"about.hash": "Build Hashwert:",
|
||||
"about.hashee": "EE Build Hashwert:",
|
||||
"about.hashwebapp": "Webapp-Build-Hash:",
|
||||
"about.licensed": "Lizenziert für:",
|
||||
"about.notice": "Mattermost wird durch Open Source Software möglich gemacht, die in unseren <linkServer>Server-</linkServer>, <linkDesktop>Desktop-</linkDesktop> und <linkMobile>mobilen</linkMobile> Apps verwendet wird.",
|
||||
"about.privacy": "Datenschutzbedingungen",
|
||||
@@ -1338,8 +1337,9 @@
|
||||
"admin.license.renewalCard.reviewNumbers": "Überprüfe deine Zahlen unten, um sicherzustellen, dass du für die richtige Anzahl von Benutzern erneuerst.",
|
||||
"admin.license.renewalCard.usersNumbers": "**Aktive Benutzer:** {activeUsersNum}",
|
||||
"admin.license.title": "Edition und Lizenz",
|
||||
"admin.license.trial-request.accept-terms": "Indem ich auf <strong>Starte Testl</strong> klicke, stimme ich dem <linkEvaluation>Mattermost Software und Services License Agreement</linkEvaluation>, <linkPrivacy>Datenschutz-Richtlinien</linkPrivacy> und dem Erhalt von Produkt-E-Mails zu.",
|
||||
"admin.license.trial-request.accept-terms": "Indem ich auf <strong>Starte Test</strong> klicke, stimme ich dem <linkEvaluation>Mattermost Software und Services License Agreement</linkEvaluation>, <linkPrivacy>Datenschutz-Richtlinien</linkPrivacy> und dem Erhalt von Produkt-E-Mails zu.",
|
||||
"admin.license.trial-request.embargoed": "Wir konnten die Anfrage auf Grund von Embargo-Beschränkungen für bestimmte Länder nicht verarbeiten. <link>Lerne mehr in unserer Dokumentation</link>, oder kontaktiere legal@mattermost.com für Fragen rund um Exportbeschränkungen.",
|
||||
"admin.license.trial-request.embargoed.button": "Schliessen",
|
||||
"admin.license.trial-request.startTrial": "Starte Test",
|
||||
"admin.license.trial-request.title": "Teste Mattermost Enterprise Edition kostenlos für die nächsten 30 Tage. Keine Kaufverpflichtung oder Kreditkarte erforderlich. ",
|
||||
"admin.license.trialCard.contactSales": "Vertrieb kontaktieren",
|
||||
@@ -2563,6 +2563,9 @@
|
||||
"admin.webserverModeUncompressed": "Unkomprimiert",
|
||||
"admin.webserverModeUncompressedDescription": "Der Mattermost-Server wird statische Dateien unkomprimiert bereitstellen.",
|
||||
"admin_settings.save_unsaved_changes": "Bitte speichere ungespeicherte Änderungen zuerst",
|
||||
"air_gapped_modal.close": "Schliessen",
|
||||
"air_gapped_modal.description": "Um deinen Test zu starten, besuche bitte {link} und fordere einen Testschlüssel an.",
|
||||
"air_gapped_modal.title": "Testschlüssel anfordern",
|
||||
"alert_banner.tooltipCloseBtn": "Schliessen",
|
||||
"analytics.chart.loading": "Lade...",
|
||||
"analytics.chart.meaningful": "Nicht genügend Daten für eine aussagekräftige Darstellung.",
|
||||
@@ -2648,7 +2651,7 @@
|
||||
"app.channel.post_update_channel_purpose_message.removed": "{username} hat den Kanalzweck entfernt (war: {old})",
|
||||
"app.channel.post_update_channel_purpose_message.updated_from": "{username} hat den Kanalzweck aktualisiert von: {old} auf: {new}",
|
||||
"app.channel.post_update_channel_purpose_message.updated_to": "{username} hat den Kanalzweck geändert zu: {new}",
|
||||
"app.plugin.marketplace_plugins.app_error": "Fehler beim Verbinden mit dem Marktplatz-Server. Bitte überprüfe deine Einstellungen in der [Systemkonsole]({siteURL}/admin_console/plugins/plugin_management).",
|
||||
"app_bar.marketplace": "App-Marktplatz",
|
||||
"apps.error": "Fehler: {error}",
|
||||
"apps.error.command.field_missing": "Erforderliche Felder fehlen: `{fieldName}`.",
|
||||
"apps.error.command.same_channel": "Kanal wiederholt für Feld `{fieldName}`: `{option}`.",
|
||||
@@ -3437,6 +3440,9 @@
|
||||
"filtered_user_list.userStatus": "Benutzerstatus:",
|
||||
"flag_post.flag": "Zur Nachverfolgung markieren",
|
||||
"flag_post.unflag": "Markierung entfernen",
|
||||
"footer_pagination.count": "Zeige {startCount, number}-{endCount, number} von {total, number}",
|
||||
"footer_pagination.next": "Weiter",
|
||||
"footer_pagination.prev": "Zurück",
|
||||
"forward_post_button.label": "Weiterleiten",
|
||||
"forward_post_modal.button.cancel": "Abbrechen",
|
||||
"forward_post_modal.button.forward": "Weiterleiten",
|
||||
@@ -4081,7 +4087,7 @@
|
||||
"mark_all_threads_as_read_modal.title": "Alle deine Unterhaltungen als gelesen markieren?",
|
||||
"marketplace_command.disabled": "Der Marktplatz ist deaktiviert. Bitte kontaktiere deinen Systemadmin für Details.",
|
||||
"marketplace_command.no_permission": "Du hast nicht die erforderlichen Berechtigungen um auf den Marktplatz zuzugreifen.",
|
||||
"marketplace_list.count_total_page": "{startCount, number} - {endCount, number} {total, plural, one {Plugin} other {Plugins}} von insgesamt {total, number}",
|
||||
"marketplace_modal.app_error": "Fehler beim Verbinden mit dem Marktplatz-Server. Bitte überprüfe deine Einstellungen in der <linkConsole>Systemkonsole</linkConsole>.",
|
||||
"marketplace_modal.install_plugins": "Plugins installieren",
|
||||
"marketplace_modal.installing": "Installiere...",
|
||||
"marketplace_modal.list.configure": "Konfigurieren",
|
||||
@@ -4099,12 +4105,13 @@
|
||||
"marketplace_modal.list.update_confirmation.message.warning_major_version": "Dieses Update kann inkompatible Änderungen enthalten.",
|
||||
"marketplace_modal.list.update_confirmation.message.warning_major_version_with_release_notes": "Dieses Update kann inkompatible Änderungen enthalten. Prüfe die [Release Notes](!{releaseNotesUrl}) vor dem Upgrade.",
|
||||
"marketplace_modal.list.update_confirmation.title": "Plugin-Update bestätigen",
|
||||
"marketplace_modal.no_plugins": "Zurzeit sind keine Plugins verfügbar.",
|
||||
"marketplace_modal.no_plugins_installed": "Du hast keine Plugins installiert.",
|
||||
"marketplace_modal.search": "Plugin-Marktplatz durchsuchen",
|
||||
"marketplace_modal.no_plugins": "Keine Plugins gefunden",
|
||||
"marketplace_modal.no_plugins_installed": "Keine installierten Plugins gefunden",
|
||||
"marketplace_modal.search": "Marktplatz durchsuchen",
|
||||
"marketplace_modal.tabs.all_listing": "Alle",
|
||||
"marketplace_modal.tabs.installed_listing": "Installierte",
|
||||
"marketplace_modal.title": "Plugin-Marktplatz",
|
||||
"marketplace_modal.tabs.installed_listing": "Installiert ({count})",
|
||||
"marketplace_modal.title": "App-Marktplatz",
|
||||
"marketplace_modal_list.no_plugins_filter": "Keine Ergebnisse für \"{filter}\"",
|
||||
"members_popover.button.message": "Nachricht",
|
||||
"menu.cloudFree.enterpriseTrialDescription": "Dein Test ist aktiv bis zum {trialEndDay}. Entdecke unsere besten Enterprise Funktionen. <openModalLink>Erfahre mehr</openModalLink>",
|
||||
"menu.cloudFree.enterpriseTrialTitle": "Enterprise Test",
|
||||
@@ -4156,22 +4163,13 @@
|
||||
"modal.manual_status.title_offline": "Dein Status wurde auf \"Offline\" gesetzt",
|
||||
"modal.manual_status.title_ooo": "Dein Status ist auf \"Nicht im Büro\" gesetzt",
|
||||
"more.details": "Mehr Details",
|
||||
"more_channels.channel_purpose": "Kanal-Informationen: Mitgliedschaftsindikator: Beigetreten, Mitglieder {memberCount}, Zweck: {channelPurpose}",
|
||||
"more_channels.count": "{count} Ergebnisse",
|
||||
"more_channels.count_one": "1 Ergebnis",
|
||||
"more_channels.count_zero": "Keine Ergebnisse",
|
||||
"more_channels.create": "Kanal erstellen",
|
||||
"more_channels.hide_joined": "Verbundene Kanäle ausblenden",
|
||||
"more_channels.hide_joined_checked": "Kontrollkästchen Verbundene Kanäle ausblenden, aktiviert",
|
||||
"more_channels.hide_joined_not_checked": "Kontrollkästchen Verbundene Kanäle ausblenden, deaktiviert",
|
||||
"more_channels.joined": "Verknüpft",
|
||||
"more_channels.membership_indicator": "Mitgliedschaftsindikator: Beigetreten",
|
||||
"more_channels.createClick": "Klicke auf 'Neuen Kanal erstellen' um einen Neuen zu erzeugen",
|
||||
"more_channels.join": "Beitreten",
|
||||
"more_channels.joining": "Beitreten...",
|
||||
"more_channels.next": "Weiter",
|
||||
"more_channels.noArchived": "Keine archivierten Kanäle",
|
||||
"more_channels.noMore": "Keine Ergebnisse für \"{text}\"",
|
||||
"more_channels.noPublic": "Keine öffentlichen Kanäle",
|
||||
"more_channels.prev": "Zurück",
|
||||
"more_channels.searchError": "Versuche, nach anderen Stichworten zu suchen, auf Tippfehlern zu prüfen oder die Filter anzupassen.",
|
||||
"more_channels.show_archived_channels": "Anzeigen: Archivierte Kanäle",
|
||||
"more_channels.show_public_channels": "Anzeigen: Öffentliche Kanäle",
|
||||
"more_channels.title": "Weitere Kanäle",
|
||||
@@ -4236,7 +4234,7 @@
|
||||
"navbar_dropdown.logout": "Abmelden",
|
||||
"navbar_dropdown.manageGroups": "Gruppen verwalten",
|
||||
"navbar_dropdown.manageMembers": "Mitglieder verwalten",
|
||||
"navbar_dropdown.marketplace": "Plugin-Marktplatz",
|
||||
"navbar_dropdown.marketplace": "App-Marktplatz",
|
||||
"navbar_dropdown.menuAriaLabel": "Hauptmenü",
|
||||
"navbar_dropdown.nativeApps": "Apps herunterladen",
|
||||
"navbar_dropdown.profileSettings": "Profil",
|
||||
@@ -4294,9 +4292,10 @@
|
||||
"onboardingTask.checklist.downloads": "Jetzt, da du alles konfiguriert hast, <link>lade unsere Apps runter.</link>",
|
||||
"onboardingTask.checklist.higher_security_features": "Interessiert an unseren Hochsicherheitsfunktionen?",
|
||||
"onboardingTask.checklist.main_subtitle": "Legen wir los.",
|
||||
"onboardingTask.checklist.no_thanks": "Nein, danke",
|
||||
"onboardingTask.checklist.start_enterprise_now": "Starte jetzt Deinen kostenfreien Enterprise-Test!",
|
||||
"onboardingTask.checklist.task_complete_your_profile": "Vervollständige dein Profil.",
|
||||
"onboardingTask.checklist.task_create_from_work_template": "Erstellen mit einer Vorlage - richte einen Kanal mit verknüpften Boards und Playbooks ein.",
|
||||
"onboardingTask.checklist.task_create_from_work_template": "Erstellen aus einer Vorlage",
|
||||
"onboardingTask.checklist.task_download_mm_apps": "Lade die Desktop-, Tablet- und Handy-Apps runter.",
|
||||
"onboardingTask.checklist.task_explore_other_tools_in_platform": "Erkunde andere Tools auf der Plattform.",
|
||||
"onboardingTask.checklist.task_invite_team_members": "Lade Teammitglieder in den Arbeitsbereich ein.",
|
||||
@@ -4868,7 +4867,7 @@
|
||||
"sidebar.directchannel.you": "{displayname} (Sie)",
|
||||
"sidebar.menu.item.notSelected": "nicht ausgewählt",
|
||||
"sidebar.menu.item.selected": "ausgewählt",
|
||||
"sidebar.openDirectMessage": "Direktnachricht senden",
|
||||
"sidebar.openDirectMessage": "Direktnachricht öffnen",
|
||||
"sidebar.show": "Anzeigen",
|
||||
"sidebar.sort": "Sortierung",
|
||||
"sidebar.sortedByRecencyLabel": "Letzte Aktivität",
|
||||
@@ -4883,6 +4882,8 @@
|
||||
"sidebar.types.favorites": "FAVORITEN",
|
||||
"sidebar.types.unreads": "UNGELESENE",
|
||||
"sidebar.unreads": "Weitere Ungelesene",
|
||||
"sidebar_left.addChannelsCta": "Kanäle hinzufügen",
|
||||
"sidebar_left.add_channel_cta_dropdown.dropdownAriaLabel": "Kanal-Dropdown hinzufügen",
|
||||
"sidebar_left.add_channel_dropdown.browseChannels": "Kanäle durchsuchen",
|
||||
"sidebar_left.add_channel_dropdown.browseOrCreateChannels": "Kanäle erstellen oder durchsuchen",
|
||||
"sidebar_left.add_channel_dropdown.createCategory": "Neue Kategorie erstellen",
|
||||
@@ -4891,7 +4892,7 @@
|
||||
"sidebar_left.add_channel_dropdown.invitePeople": "Personen einladen",
|
||||
"sidebar_left.add_channel_dropdown.invitePeopleExtraText": "Personen zum Team hinzufügen",
|
||||
"sidebar_left.add_channel_dropdown.work_template": "Aus einer Vorlage erstellen",
|
||||
"sidebar_left.add_channel_dropdown.work_template_extra": "Richte einen Kanal mit verknüpften Boards und Playbooks ein",
|
||||
"sidebar_left.add_channel_dropdown.work_template_extra": "Verknüpfe Kanäle, Boards und Playbooks miteinander",
|
||||
"sidebar_left.channel_filter.filterByUnread": "Gefiltert durch ungelesen",
|
||||
"sidebar_left.channel_filter.filterUnreadAria": "Ungelesen Filter",
|
||||
"sidebar_left.channel_filter.showAllChannels": "Alle Kanäle anzeigen",
|
||||
@@ -4931,6 +4932,7 @@
|
||||
"sidebar_left.sidebar_channel_menu.unfavoriteChannel": "Nicht favorisieren",
|
||||
"sidebar_left.sidebar_channel_menu.unmuteChannel": "Stummschaltung aufheben",
|
||||
"sidebar_left.sidebar_channel_menu.unmuteConversation": "Stummschaltung aufheben",
|
||||
"sidebar_left.sidebar_channel_navigator.addChannelsCta": "Kanäle hinzufügen",
|
||||
"sidebar_left.sidebar_channel_navigator.inviteUsers": "Benutzer einladen",
|
||||
"sidebar_right_menu.console": "Systemkonsole",
|
||||
"sidebar_right_menu.flagged": "Markierte Nachrichten",
|
||||
@@ -4979,13 +4981,21 @@
|
||||
"start_trial.modal.gettingTrial": "Hole Test...",
|
||||
"start_trial.modal.loaded": "Geladen!",
|
||||
"start_trial.modal.loading": "Lade...",
|
||||
"start_trial.modal_body": "Greife auf alle Plattform-Funktionen, inklusive erweiterter Sicherheit- und Unternehmens-Compliance, zu.",
|
||||
"start_trial.modal_btn.nottnow": "Nicht jetzt",
|
||||
"start_trial.modal_btn.start": "Starte dem freien 30-Tage Test",
|
||||
"start_trial.modal_btn.start_free_trial": "Kostenlose 30-Tage-Testversion starten",
|
||||
"start_trial.modal_title": "Starte jetzt deine kostenlose Enterprise-Testversion",
|
||||
"start_trial.tutorialTip.desc": "Probiere unsere am meisten gefragten Premium Funktionen: Lege den Benutzerzugriff mit Gast-Konten fest, automatisiere Compliance Berichte und sende sichere, mobile Nur-ID Push-Nachrichten.",
|
||||
"start_trial.tutorialTip.title": "Teste kostenfrei Premium Funktionen",
|
||||
"start_trial_form.company_name": "Firmenname",
|
||||
"start_trial_form.company_size": "Größe des Unternehmens",
|
||||
"start_trial_form.disclaimer": "Mit Auswählen von Starte Test, stimme ich dem <agreement>Mattermost Software Evaluation Agreement</agreement> und der <privacypolicy>Datenschutzrichtlinie</privacypolicy> zu, und erhalte Produktemails.",
|
||||
"start_trial_form.email": "Geschäftliche E-Mail",
|
||||
"start_trial_form.invalid_business_email": "Bitte gib eine gültige geschäftliche E-Mail-Adresse ein.",
|
||||
"start_trial_form.modal_body": "Nur ein paar kurze Hinweise, die uns helfen, deine Testerfahrung individuell zu gestalten",
|
||||
"start_trial_form.modal_btn.start": "Test starten",
|
||||
"start_trial_form.modal_title": "Test starten",
|
||||
"start_trial_form.name": "Name",
|
||||
"start_trial_form_modal.failureModal.subtitle": "Es gab ein Problem bei der Bearbeitung deiner Testanfrage.",
|
||||
"start_trial_form_modal.failureModal.subtitle2": "Bitte versuche es erneut oder kontaktiere den Support.",
|
||||
"start_trial_form_modal.failureModal.title": "Bitte versuche es erneut",
|
||||
"status_dropdown.dnd_sub_menu_header": "Benachrichtigungen ausschalten bis:",
|
||||
"status_dropdown.dnd_sub_menu_item.custom": "Benutzerdefiniert",
|
||||
"status_dropdown.dnd_sub_menu_item.one_hour": "Eine Stunde",
|
||||
@@ -5669,6 +5679,7 @@
|
||||
"welcome_post_renderer.user_message.first_paragraph": "Mattermost ist eine Open-Source-Plattform für sichere Kommunikation, Zusammenarbeit und Orchestrierung der Arbeit über Tools und Teams hinweg.",
|
||||
"welcome_post_renderer.user_message.second_paragraph": "Hier findest Du eine Liste von Befehlen, die Du verwenden kannst, um sich mit der Plattform vertraut zu machen.",
|
||||
"welcome_post_renderer.user_message.title": "Willkommen bei Mattermost! :rocket:",
|
||||
"widget.input.clear": "Leeren",
|
||||
"widget.input.required": "Dieses Feld wird benötigt",
|
||||
"widget.passwordInput.createPassword": "Wähle ein Passwort",
|
||||
"widget.passwordInput.password": "Passwort",
|
||||
@@ -5687,7 +5698,7 @@
|
||||
"work_templates.customize.name_label_channels_playbooks": "Benenne deinen Kanal und Playbook",
|
||||
"work_templates.customize.private_playbook_license_issue": "Für private Playbooks ist eine Enterprise-Lizenz erforderlich.",
|
||||
"work_templates.customize.visibility_title": "Wer soll Zugang dazu haben?",
|
||||
"work_templates.menu.modal_title": "Beginne mit einer Vorlage",
|
||||
"work_templates.menu.modal_title": "Erstellen aus einer Vorlage",
|
||||
"work_templates.menu.quick_use": "Schneller Einsatz",
|
||||
"work_templates.menu.template_title": "VORLAGE",
|
||||
"work_templates.menu.usecase_boards_count": "{boardsCount, plural, =1 {# Board} other {# Boards}}",
|
||||
|
||||
@@ -2653,7 +2653,6 @@
|
||||
"app.channel.post_update_channel_purpose_message.removed": "{username} removed the channel purpose (was: {old})",
|
||||
"app.channel.post_update_channel_purpose_message.updated_from": "{username} updated the channel purpose from: {old} to: {new}",
|
||||
"app.channel.post_update_channel_purpose_message.updated_to": "{username} updated the channel purpose to: {new}",
|
||||
"app.plugin.marketplace_plugins.app_error": "Error connecting to the marketplace server. Please check your settings in the [System Console]({siteURL}/admin_console/plugins/plugin_management).",
|
||||
"apps.error": "Error: {error}",
|
||||
"apps.error.command.field_missing": "Required fields missing: `{fieldName}`.",
|
||||
"apps.error.command.same_channel": "Channel repeated for field `{fieldName}`: `{option}`.",
|
||||
@@ -3442,6 +3441,9 @@
|
||||
"filtered_user_list.userStatus": "User Status:",
|
||||
"flag_post.flag": "Save",
|
||||
"flag_post.unflag": "Remove from Saved",
|
||||
"footer_pagination.count": "Showing {startCount, number}-{endCount, number} of {total, number}",
|
||||
"footer_pagination.next": "Next",
|
||||
"footer_pagination.prev": "Previous",
|
||||
"forward_post_button.label": "Forward",
|
||||
"forward_post_modal.button.cancel": "Cancel",
|
||||
"forward_post_modal.button.forward": "Forward",
|
||||
@@ -4086,8 +4088,9 @@
|
||||
"mark_all_threads_as_read_modal.title": "Mark all your threads as read?",
|
||||
"marketplace_command.disabled": "The marketplace is disabled. Please contact your System Administrator for details.",
|
||||
"marketplace_command.no_permission": "You do not have the appropriate permissions to access the marketplace.",
|
||||
"marketplace_list.count_total_page": "{startCount, number} - {endCount, number} {total, plural, one {plugin} other {plugins}} of {total, number} total",
|
||||
"marketplace_modal.install_plugins": "Install Plugins",
|
||||
"marketplace_modal_list.no_plugins_filter": "No results for \"{filter}\"",
|
||||
"marketplace_modal.app_error": "Error connecting to the marketplace server. Please check your settings in the <linkConsole>System Console</linkConsole>.",
|
||||
"marketplace_modal.install_plugins": "Install plugins",
|
||||
"marketplace_modal.installing": "Installing...",
|
||||
"marketplace_modal.list.configure": "Configure",
|
||||
"marketplace_modal.list.configure.plugin": "Configure {plugin}",
|
||||
@@ -4104,12 +4107,12 @@
|
||||
"marketplace_modal.list.update_confirmation.message.warning_major_version": "This update may contain breaking changes.",
|
||||
"marketplace_modal.list.update_confirmation.message.warning_major_version_with_release_notes": "This update may contain breaking changes. Consult the [release notes](!{releaseNotesUrl}) before upgrading.",
|
||||
"marketplace_modal.list.update_confirmation.title": "Confirm Plugin Update",
|
||||
"marketplace_modal.no_plugins": "There are no plugins available at this time.",
|
||||
"marketplace_modal.no_plugins_installed": "You do not have any plugins installed.",
|
||||
"marketplace_modal.search": "Search Marketplace",
|
||||
"marketplace_modal.no_plugins": "No plugins found",
|
||||
"marketplace_modal.no_plugins_installed": "No plugins installed found",
|
||||
"marketplace_modal.search": "Search marketplace",
|
||||
"marketplace_modal.tabs.all_listing": "All",
|
||||
"marketplace_modal.tabs.installed_listing": "Installed",
|
||||
"marketplace_modal.title": "Marketplace",
|
||||
"marketplace_modal.tabs.installed_listing": "Installed ({count})",
|
||||
"marketplace_modal.title": "App Marketplace",
|
||||
"members_popover.button.message": "message",
|
||||
"menu.cloudFree.enterpriseTrialDescription": "Your trial is active until {trialEndDay}. Discover our top Enterprise features. <openModalLink>Learn more</openModalLink>",
|
||||
"menu.cloudFree.enterpriseTrialTitle": "Enterprise Trial",
|
||||
@@ -4252,6 +4255,9 @@
|
||||
"navbar.viewPinnedPosts": "View Pinned Posts",
|
||||
"newChannelWithBoard.tutorialTip.description": "The board you just created can be quickly accessed by clicking on the Boards icon in the App bar. You can view the boards that are linked to this channel in the right-hand sidebar and open one in full view.",
|
||||
"newChannelWithBoard.tutorialTip.title": "Access linked boards from the App Bar",
|
||||
"newsletter_optin.checkmark.text": "I would like to receive Mattermost security updates via newsletter. Data <a>Terms and Conditions</a> apply",
|
||||
"newsletter_optin.desc": "Sign up at <a>{link}</a>.",
|
||||
"newsletter_optin.title": "Interested in receiving Mattermost security updates via newsletter?",
|
||||
"next_steps_view.welcomeToMattermost": "Welcome to Mattermost",
|
||||
"no_results.channel_files_filtered.subtitle": "This channel doesn't contains any file with the selected file format.",
|
||||
"no_results.channel_files_filtered.title": "No files found",
|
||||
@@ -4309,10 +4315,10 @@
|
||||
"onboardingTask.checklist.downloads": "Now that you’re all set up, <link>download our apps.</link>",
|
||||
"onboardingTask.checklist.higher_security_features": "Interested in our higher-security features?",
|
||||
"onboardingTask.checklist.main_subtitle": "Let's get up and running.",
|
||||
"onboardingTask.checklist.no_tanks": "No, thanks",
|
||||
"onboardingTask.checklist.no_thanks": "No, thanks",
|
||||
"onboardingTask.checklist.start_enterprise_now": "Start your free Enterprise trial now!",
|
||||
"onboardingTask.checklist.task_complete_your_profile": "Complete your profile.",
|
||||
"onboardingTask.checklist.task_create_from_work_template": "Create from a template - set up a channel with linked boards and playbooks.",
|
||||
"onboardingTask.checklist.task_create_from_work_template": "Create from a template",
|
||||
"onboardingTask.checklist.task_download_mm_apps": "Download the Desktop and Mobile Apps.",
|
||||
"onboardingTask.checklist.task_explore_other_tools_in_platform": "Explore other tools in the platform.",
|
||||
"onboardingTask.checklist.task_invite_team_members": "Invite team members to the workspace.",
|
||||
@@ -4880,15 +4886,15 @@
|
||||
"shortcuts.team_nav.switcher": "Navigate to a specific team:\tCtrl|Alt|[1-9]",
|
||||
"shortcuts.team_nav.switcher.mac": "Navigate to a specific team:\t⌘|⌥|[1-9]",
|
||||
"sidebar_left.add_channel_cta_dropdown.dropdownAriaLabel": "Add Channel Dropdown",
|
||||
"sidebar_left.add_channel_dropdown.browseChannels": "Browse Channels",
|
||||
"sidebar_left.add_channel_dropdown.browseChannels": "Browse channels",
|
||||
"sidebar_left.add_channel_dropdown.browseOrCreateChannels": "Browse or create channels",
|
||||
"sidebar_left.add_channel_dropdown.createCategory": "Create New Category",
|
||||
"sidebar_left.add_channel_dropdown.createNewChannel": "Create New Channel",
|
||||
"sidebar_left.add_channel_dropdown.createCategory": "Create new category",
|
||||
"sidebar_left.add_channel_dropdown.createNewChannel": "Create new channel",
|
||||
"sidebar_left.add_channel_dropdown.dropdownAriaLabel": "Add Channel Dropdown",
|
||||
"sidebar_left.add_channel_dropdown.invitePeople": "Invite People",
|
||||
"sidebar_left.add_channel_dropdown.invitePeople": "Invite people",
|
||||
"sidebar_left.add_channel_dropdown.invitePeopleExtraText": "Add people to the team",
|
||||
"sidebar_left.add_channel_dropdown.work_template": "Create from a template",
|
||||
"sidebar_left.add_channel_dropdown.work_template_extra": "Set up a channel with linked boards, and playbooks",
|
||||
"sidebar_left.add_channel_dropdown.work_template_extra": "Link channels, boards, and playbooks together",
|
||||
"sidebar_left.addChannelsCta": "Add channels",
|
||||
"sidebar_left.channel_filter.filterByUnread": "Filter by unread",
|
||||
"sidebar_left.channel_filter.filterUnreadAria": "unreads filter",
|
||||
@@ -4940,7 +4946,7 @@
|
||||
"sidebar.directchannel.you": "{displayname} (you)",
|
||||
"sidebar.menu.item.notSelected": "not selected",
|
||||
"sidebar.menu.item.selected": "selected",
|
||||
"sidebar.openDirectMessage": "Open a Direct Message",
|
||||
"sidebar.openDirectMessage": "Open a direct message",
|
||||
"sidebar.show": "Show",
|
||||
"sidebar.sort": "Sort",
|
||||
"sidebar.sortedByRecencyLabel": "Recent activity",
|
||||
@@ -5697,6 +5703,7 @@
|
||||
"welcome_post_renderer.user_message.first_paragraph": "Mattermost is an open source platform for secure communication, collaboration, and orchestration of work across tools and teams.",
|
||||
"welcome_post_renderer.user_message.second_paragraph": "Here is a list of commands to use to try and get familiar with the platform.",
|
||||
"welcome_post_renderer.user_message.title": "Welcome to Mattermost! :rocket:",
|
||||
"widget.input.clear": "Clear",
|
||||
"widget.input.required": "This field is required",
|
||||
"widget.passwordInput.createPassword": "Choose a Password",
|
||||
"widget.passwordInput.password": "Password",
|
||||
@@ -5715,7 +5722,7 @@
|
||||
"work_templates.customize.name_label_channels_playbooks": "Name your channel and playbook",
|
||||
"work_templates.customize.private_playbook_license_issue": "Private playbooks requires an Enterprise license.",
|
||||
"work_templates.customize.visibility_title": "Who should have access to this?",
|
||||
"work_templates.menu.modal_title": "Start from a template",
|
||||
"work_templates.menu.modal_title": "Create from a template",
|
||||
"work_templates.menu.quick_use": "Quick use",
|
||||
"work_templates.menu.template_title": "TEMPLATE",
|
||||
"work_templates.menu.usecase_boards_count": "{boardsCount, plural, =1 {# board} other {# boards}}",
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
"about.enterpriseEditione1": "Enterprise Edition",
|
||||
"about.hash": "Build Hash:",
|
||||
"about.hashee": "EE Build Hash:",
|
||||
"about.hashwebapp": "Webapp Build Hash:",
|
||||
"about.licensed": "Licensed to:",
|
||||
"about.notice": "Mattermost is made possible by the open source software used in our <linkServer>server</linkServer>, <linkDesktop>desktop</linkDesktop> and <linkMobile>mobile</linkMobile> apps.",
|
||||
"about.privacy": "Privacy Policy",
|
||||
@@ -2644,7 +2643,6 @@
|
||||
"app.channel.post_update_channel_purpose_message.removed": "{username} removed the channel purpose (was: {old})",
|
||||
"app.channel.post_update_channel_purpose_message.updated_from": "{username} updated the channel purpose from: {old} to: {new}",
|
||||
"app.channel.post_update_channel_purpose_message.updated_to": "{username} updated the channel purpose to: {new}",
|
||||
"app.plugin.marketplace_plugins.app_error": "Error connecting to the marketplace server. Please check your settings in the [System Console]({siteURL}/admin_console/plugins/plugin_management).",
|
||||
"apps.error": "Error: {error}",
|
||||
"apps.error.command.field_missing": "Required fields missing: `{fieldName}`.",
|
||||
"apps.error.command.same_channel": "Channel repeated for field `{fieldName}`: `{option}`.",
|
||||
@@ -4077,7 +4075,6 @@
|
||||
"mark_all_threads_as_read_modal.title": "Mark all your threads as read?",
|
||||
"marketplace_command.disabled": "The marketplace is disabled. Please contact your System Administrator for details.",
|
||||
"marketplace_command.no_permission": "You do not have the appropriate permissions to access the marketplace.",
|
||||
"marketplace_list.count_total_page": "{startCount, number} - {endCount, number} {total, plural, one {plugin} other {plugins}} of {total, number} total",
|
||||
"marketplace_modal.install_plugins": "Install Plugins",
|
||||
"marketplace_modal.installing": "Installing...",
|
||||
"marketplace_modal.list.configure": "Configure",
|
||||
@@ -4280,7 +4277,7 @@
|
||||
"onboardingTask.checklist.main_subtitle": "Let's get up and running.",
|
||||
"onboardingTask.checklist.start_enterprise_now": "Start your free Enterprise trial now!",
|
||||
"onboardingTask.checklist.task_complete_your_profile": "Complete your profile",
|
||||
"onboardingTask.checklist.task_create_from_work_template": "Create from a template - set up a channel with linked boards and playbooks.",
|
||||
"onboardingTask.checklist.task_create_from_work_template": "Create from a template",
|
||||
"onboardingTask.checklist.task_download_mm_apps": "Download the Desktop and Mobile Apps",
|
||||
"onboardingTask.checklist.task_explore_other_tools_in_platform": "Explore other tools in the platform.",
|
||||
"onboardingTask.checklist.task_invite_team_members": "Invite team members to the workspace",
|
||||
@@ -4831,7 +4828,7 @@
|
||||
"sidebar.directchannel.you": "{displayname} (you)",
|
||||
"sidebar.menu.item.notSelected": "not selected",
|
||||
"sidebar.menu.item.selected": "selected",
|
||||
"sidebar.openDirectMessage": "Open a Direct Message",
|
||||
"sidebar.openDirectMessage": "Open a direct message",
|
||||
"sidebar.show": "Show",
|
||||
"sidebar.sort": "Sort",
|
||||
"sidebar.sortedByRecencyLabel": "Recent activity",
|
||||
@@ -4846,15 +4843,15 @@
|
||||
"sidebar.types.favorites": "FAVOURITES",
|
||||
"sidebar.types.unreads": "UNREADS",
|
||||
"sidebar.unreads": "More unreads",
|
||||
"sidebar_left.add_channel_dropdown.browseChannels": "Browse Channels",
|
||||
"sidebar_left.add_channel_dropdown.browseChannels": "Browse channels",
|
||||
"sidebar_left.add_channel_dropdown.browseOrCreateChannels": "Browse or create channels",
|
||||
"sidebar_left.add_channel_dropdown.createCategory": "Create New Category",
|
||||
"sidebar_left.add_channel_dropdown.createNewChannel": "Create New Channel",
|
||||
"sidebar_left.add_channel_dropdown.createCategory": "Create new category",
|
||||
"sidebar_left.add_channel_dropdown.createNewChannel": "Create new channel",
|
||||
"sidebar_left.add_channel_dropdown.dropdownAriaLabel": "Add Channel Dropdown",
|
||||
"sidebar_left.add_channel_dropdown.invitePeople": "Invite People",
|
||||
"sidebar_left.add_channel_dropdown.invitePeople": "Invite people",
|
||||
"sidebar_left.add_channel_dropdown.invitePeopleExtraText": "Add people to the team",
|
||||
"sidebar_left.add_channel_dropdown.work_template": "Create from a template",
|
||||
"sidebar_left.add_channel_dropdown.work_template_extra": "Set up a channel with linked boards, and playbooks",
|
||||
"sidebar_left.add_channel_dropdown.work_template_extra": "Link channels, boards, and playbooks together",
|
||||
"sidebar_left.channel_filter.filterByUnread": "Filter by unread",
|
||||
"sidebar_left.channel_filter.filterUnreadAria": "unreads filter",
|
||||
"sidebar_left.channel_filter.showAllChannels": "Show all channels",
|
||||
@@ -4942,11 +4939,7 @@
|
||||
"start_trial.modal.gettingTrial": "Getting Trial...",
|
||||
"start_trial.modal.loaded": "Loaded!",
|
||||
"start_trial.modal.loading": "Loading...",
|
||||
"start_trial.modal_body": "Access all platform features including advanced security and enterprise compliance.",
|
||||
"start_trial.modal_btn.nottnow": "Not now",
|
||||
"start_trial.modal_btn.start": "Start a free 30-day trial",
|
||||
"start_trial.modal_btn.start_free_trial": "Start a free 30-day trial",
|
||||
"start_trial.modal_title": "Start your free Enterprise trial now",
|
||||
"start_trial.tutorialTip.desc": "Explore our most requested premium features. Determine user access with Guest Accounts, automate compliance reports, and send secure ID-only mobile push notifications.",
|
||||
"start_trial.tutorialTip.title": "Try our premium features for free",
|
||||
"status_dropdown.dnd_sub_menu_header": "Disable notifications until:",
|
||||
@@ -5650,7 +5643,7 @@
|
||||
"work_templates.customize.name_label_channels_playbooks": "Name your channel and playbook",
|
||||
"work_templates.customize.private_playbook_license_issue": "Private playbooks requires an Enterprise license.",
|
||||
"work_templates.customize.visibility_title": "Who should have access to this?",
|
||||
"work_templates.menu.modal_title": "Start from a template",
|
||||
"work_templates.menu.modal_title": "Create from a template",
|
||||
"work_templates.menu.quick_use": "Quick use",
|
||||
"work_templates.menu.template_title": "TEMPLATE",
|
||||
"work_templates.menu.usecase_boards_count": "{boardsCount, plural, =1 {# board} other {# boards}}",
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
"about.enterpriseEditione1": "Edición Empresarial E1",
|
||||
"about.hash": "Hash de compilación:",
|
||||
"about.hashee": "Hash de compilación de EE:",
|
||||
"about.hashwebapp": "Hash de compilación de la App Web:",
|
||||
"about.licensed": "Licenciado a:",
|
||||
"about.notice": "Mattermost es hecho posible gracias a software de código libre utilizado en nuestro <linkServer>servidor</linkServer>, <linkDesktop>desktop</linkDesktop> y apps <linkMobile>móviles</linkMobile>.",
|
||||
"about.privacy": "Política de Privacidad",
|
||||
@@ -2567,7 +2566,6 @@
|
||||
"app.channel.post_update_channel_purpose_message.removed": "{username} eliminó el propósito del canal (era: {old})",
|
||||
"app.channel.post_update_channel_purpose_message.updated_from": "{username} ha actualizado el propósito del canal de: {old} a: {new}",
|
||||
"app.channel.post_update_channel_purpose_message.updated_to": "{username} ha actualizado el propósito del canal a: {new}",
|
||||
"app.plugin.marketplace_plugins.app_error": "Error de conexión con el servidor del marketplace. Por favor revisa los ajustes en la [Consola del Sistema]({siteURL}/admin_console/plugins/plugin_management).",
|
||||
"apps.error": "Error: {error}",
|
||||
"apps.error.command.field_missing": "Faltan campos obligatorios: `{fieldName}`.",
|
||||
"apps.error.command.same_channel": "Canal repetido para el campo `{fieldName}`: `{option}`.",
|
||||
@@ -3828,7 +3826,6 @@
|
||||
"manage_channel_groups_modal.search_placeholder": "Buscar grupos",
|
||||
"manage_team_groups_modal.search_placeholder": "Buscar grupos",
|
||||
"mark_all_threads_as_read_modal.cancel": "Cancelar",
|
||||
"marketplace_list.count_total_page": "{startCount, number} - {endCount, number} {total, plural, one {plugin} other {plugins}} de {total, number} total",
|
||||
"marketplace_modal.install_plugins": "Instalar Plugins",
|
||||
"marketplace_modal.installing": "Instalando...",
|
||||
"marketplace_modal.list.configure": "Configurar",
|
||||
@@ -4492,11 +4489,7 @@
|
||||
"start_trial.modal.gettingTrial": "Obteniendo Prueba...",
|
||||
"start_trial.modal.loaded": "¡Cargado!",
|
||||
"start_trial.modal.loading": "Cargando...",
|
||||
"start_trial.modal_body": "Accede a todas las características de la plataforma incluyendo seguridad avanzada y cumplimiento empresarial.",
|
||||
"start_trial.modal_btn.nottnow": "Ahora no",
|
||||
"start_trial.modal_btn.start": "¡Comienza la pruba gratuita de 30 días",
|
||||
"start_trial.modal_btn.start_free_trial": "Comienza tu prueba gratuita de 30 días",
|
||||
"start_trial.modal_title": "Comienza tu prueba gratuita Enterprise ahora",
|
||||
"start_trial.tutorialTip.title": "Prueba nuestras características premium de forma gratuita",
|
||||
"status_dropdown.dnd_sub_menu_header": "Desactivar las notificaciones hasta:",
|
||||
"status_dropdown.dnd_sub_menu_item.custom": "Personalizado",
|
||||
@@ -5114,6 +5107,7 @@
|
||||
"widgets.users_emails_input.loading": "Cargando",
|
||||
"widgets.users_emails_input.no_user_found_matching": "No se ha encontrado a nadie que coincida con **{text}**. Introduce su correo electrónico para invitarle.",
|
||||
"widgets.users_emails_input.valid_email": "Agregar **{email}**",
|
||||
"work_templates.preview.section.included": "included sp",
|
||||
"workspace_limits.archived_file.archived_compact": "(archivado)",
|
||||
"workspace_limits.file_storage": "Almacenamiento de archivos",
|
||||
"workspace_limits.file_storage.short": "Archivos",
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
"about.enterpriseEditione1": "نسخه تجاری",
|
||||
"about.hash": "هش ساخت:",
|
||||
"about.hashee": "هش ساخت نسخه تجاری:",
|
||||
"about.hashwebapp": "هش بیلد وباپ:",
|
||||
"about.licensed": "مجوز داده شده به:",
|
||||
"about.notice": "Mattermost یک نرم افزار متن باز توسه یافته است . جهت بررسی نسخه ها در <linkServer>server</linkServer> و نسخه دسکتاپ در <linkDesktop>desktop</linkDesktop> و اپلیکشن موبایل در <linkMobile>mobile</linkMobile> اطلاع رسانی می شود.",
|
||||
"about.privacy": "سیاست حفظ حریم خصوصی",
|
||||
@@ -2404,7 +2403,6 @@
|
||||
"app.channel.post_update_channel_purpose_message.removed": "{username} هدف کانال را حذف کرد (بود: {قدیمی})",
|
||||
"app.channel.post_update_channel_purpose_message.updated_from": "{username} هدف کانال را از: {old} به: {new} بهروزرسانی کرد",
|
||||
"app.channel.post_update_channel_purpose_message.updated_to": "{username} هدف کانال را بهروزرسانی کرد: {new}",
|
||||
"app.plugin.marketplace_plugins.app_error": "خطا در اتصال به سرور بازار. لطفاً تنظیمات خود را در [System Console] ({siteURL}/admin_console/plugins/plugin_management) بررسی کنید.",
|
||||
"apps.error": "خطا: {error}",
|
||||
"apps.error.command.field_missing": "فیلدهای لازم وجود ندارد: `{fieldName}`.",
|
||||
"apps.error.command.same_channel": "تکرار کانال برای فیلد «{fieldName}»: «{option}».",
|
||||
@@ -3634,7 +3632,6 @@
|
||||
"manage_channel_groups_modal.search_placeholder": "جستجو در گروه ها",
|
||||
"manage_team_groups_modal.search_placeholder": "جستجو در گروه ها",
|
||||
"mark_all_threads_as_read_modal.cancel": "لغو",
|
||||
"marketplace_list.count_total_page": "{startCount, number} - {endCount, number} {total, plural, one {plugin} other {plugins}} از مجموع {total, number}",
|
||||
"marketplace_modal.install_plugins": "پلاگین ها را نصب کنید",
|
||||
"marketplace_modal.installing": "در حال نصب...",
|
||||
"marketplace_modal.list.configure": "پیکربندی کنید",
|
||||
@@ -4282,10 +4279,6 @@
|
||||
"start_trial.modal.gettingTrial": "در حال دریافت دوره آزمایشی...",
|
||||
"start_trial.modal.loaded": "لود شده!",
|
||||
"start_trial.modal.loading": "بارگذاری...",
|
||||
"start_trial.modal_body": "به تمام ویژگی های پلت فرم از جمله امنیت پیشرفته و انطباق سازمانی دسترسی داشته باشید.",
|
||||
"start_trial.modal_btn.nottnow": "الان نه",
|
||||
"start_trial.modal_btn.start": "شروع آزمایشی 30 روزه",
|
||||
"start_trial.modal_title": "اکنون آزمایشی رایگان Enterprise خود را شروع کنید",
|
||||
"status_dropdown.dnd_sub_menu_header": "غیرفعال کردن اعلان ها تا زمانی که:",
|
||||
"status_dropdown.dnd_sub_menu_item.custom": "سفارشی",
|
||||
"status_dropdown.dnd_sub_menu_item.one_hour": "1 ساعت",
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
"about.enterpriseEditione1": "Édition Entreprise",
|
||||
"about.hash": "Hash de version :",
|
||||
"about.hashee": "Hash de version EE :",
|
||||
"about.hashwebapp": "Hash de la webapp :",
|
||||
"about.licensed": "Licence accordée à :",
|
||||
"about.notice": "Mattermost est rendu possible grâce aux logiciels open source utilisés dans notre <linkServer>serveur</linkServer>, <linkDesktop>applications de bureau</linkDesktop> et <linkMobile>applications mobiles</linkMobile>.",
|
||||
"about.privacy": "Politique de respect de la vie privée",
|
||||
@@ -2508,7 +2507,6 @@
|
||||
"app.channel.post_update_channel_purpose_message.removed": "{username} a supprimé la description du canal (précédemment : {old})",
|
||||
"app.channel.post_update_channel_purpose_message.updated_from": "{username} a mis à jour la description du canal de : {old} en : {new}",
|
||||
"app.channel.post_update_channel_purpose_message.updated_to": "{username} a mis à jour la description du canal en : {new}",
|
||||
"app.plugin.marketplace_plugins.app_error": "Une erreur s'est produite lors de la connexion au serveur de place de marché. Veuillez vérifier vos paramètres dans la [Console du système]({siteURL}/admin_console/plugins/plugin_management).",
|
||||
"apps.error": "Erreur : {error}",
|
||||
"apps.error.command.field_missing": "Champs obligatoires manquants : `{fieldName}`.",
|
||||
"apps.error.command.same_channel": "Canal répété pour le champ `{fieldName}` : `{option}`.",
|
||||
@@ -3066,7 +3064,7 @@
|
||||
"edit_post.time_limit_modal.subscript": "Définit la durée durant laquelle les utilisateurs pourront modifier leur message après l'avoir envoyé.",
|
||||
"edit_post.time_limit_modal.title": "Configurer le délai global d'édition de messages",
|
||||
"email_verify.almost": "Vous avez pratiquement terminé !",
|
||||
"email_verify.failed": " Échec de l'envoi de l'e-mail de vérification.",
|
||||
"email_verify.failed": "Maxtrem271991@gmail.com",
|
||||
"email_verify.notVerifiedBody": "Veuillez vérifier votre adresse e-mail dans l'attente de la réception d'un e-mail de vérification.",
|
||||
"email_verify.resend": "Renvoyer l'e-mail",
|
||||
"email_verify.sent": " L'e-mail de vérification a été envoyé.",
|
||||
@@ -3667,7 +3665,6 @@
|
||||
"login_mfa.token": "Jeton d'authentification multi-facteurs (MFA)",
|
||||
"manage_channel_groups_modal.search_placeholder": "Rechercher des groupes",
|
||||
"manage_team_groups_modal.search_placeholder": "Rechercher des groupes",
|
||||
"marketplace_list.count_total_page": "{startCount, number} - {endCount, number} {total, plural, one {plugin} other {plugins}} sur un total de {total, number}",
|
||||
"marketplace_modal.install_plugins": "Installer des extensions",
|
||||
"marketplace_modal.installing": "Installation en cours...",
|
||||
"marketplace_modal.list.configure": "Configurer",
|
||||
@@ -4269,11 +4266,7 @@
|
||||
"start_trial.modal.gettingTrial": "Obtention d'un essai...",
|
||||
"start_trial.modal.loaded": "Chargé !",
|
||||
"start_trial.modal.loading": "Chargement...",
|
||||
"start_trial.modal_body": "Accédez à toutes les fonctionnalités de la plateforme, y compris la sécurité avancée et la conformité de l'entreprise.",
|
||||
"start_trial.modal_btn.nottnow": "Pas maintenant",
|
||||
"start_trial.modal_btn.start": "Commencer un essai gratuit de 30 jours",
|
||||
"start_trial.modal_btn.start_free_trial": "Commencez l'essai gratuit de 30 jours",
|
||||
"start_trial.modal_title": "Commencez votre essai gratuit de la version Enterprise maintenant",
|
||||
"start_trial.tutorialTip.desc": "Explorez nos fonctionnalités premium les plus demandées. Déterminez l'accès des utilisateurs avec les comptes d'invités, automatisez les rapports de conformité et envoyez des notifications push mobiles sécurisées uniquement pour les identifiants.",
|
||||
"start_trial.tutorialTip.title": "Essayez gratuitement nos fonctionnalités premium",
|
||||
"status_dropdown.dnd_sub_menu_header": "Désactiver les notifications jusqu'à :",
|
||||
@@ -4471,7 +4464,13 @@
|
||||
"user.settings.advance.sendDesc.mac": "Si activé, ⌘ + ENTRÉE envoie le message et ENTRÉE insère une nouvelle ligne.",
|
||||
"user.settings.advance.sendTitle": "Envoi des messages avec CTRL+ENTRÉE",
|
||||
"user.settings.advance.sendTitle.mac": "Envoi des messages avec ⌘+ENTRÉE",
|
||||
"user.settings.advance.startFromLeftOff": "Reprendre là où je m'étais arrêté",
|
||||
"user.settings.advance.startFromNewest": "Commencez par le message le plus récent",
|
||||
"user.settings.advance.syncDrafts.Desc": "Lorsqu'ils sont activés, les brouillons de messages sont synchronisés avec le serveur afin qu'ils soient accessibles depuis n'importe quel appareil. Lorsqu'ils sont désactivés, les brouillons de message ne sont enregistrés que localement sur l'appareil sur lequel ils sont rédigés.",
|
||||
"user.settings.advance.syncDrafts.Title": "Autoriser la synchronisation des messages en brouillon avec le serveur",
|
||||
"user.settings.advance.title": "Paramètres avancés",
|
||||
"user.settings.advance.unreadScrollPositionDesc": "Choisissez le positionnement lors d'un canal non lu. Les canaux sont toujours marqués comme lus lorsqu'ils sont consultés.",
|
||||
"user.settings.advance.unreadScrollPositionTitle": "Positionnement lors qu'un canal non lu",
|
||||
"user.settings.custom_theme.awayIndicator": "Indicateur « absent »",
|
||||
"user.settings.custom_theme.buttonBg": "Arrière-plan du bouton",
|
||||
"user.settings.custom_theme.buttonColor": "Texte de bouton",
|
||||
@@ -4875,6 +4874,8 @@
|
||||
"web.header.back": "Précédent",
|
||||
"web.header.logout": "Se déconnecter",
|
||||
"web.root.signup_info": "Toute la communication de votre équipe au même endroit, accessible de partout",
|
||||
"welcome_post_renderer.user_message.first_paragraph": "Mattermost is an open source platform for secure communication, collaboration, and orchestration of work across tools and teams.",
|
||||
"welcome_post_renderer.user_message.title": "\t ",
|
||||
"widget.input.required": "Ce champ est requis",
|
||||
"widgets.channels_input.empty": "Aucun canal trouvé",
|
||||
"widgets.channels_input.loading": "Chargement",
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
"about.enterpriseEditione1": "Enterprise Kiadás",
|
||||
"about.hash": "Kiadás ujjlenyomata:",
|
||||
"about.hashee": "EE kiadás ujjlenyomata:",
|
||||
"about.hashwebapp": "Webapp kiadás ujjlenyomat:",
|
||||
"about.licensed": "Licenc tulajdonosa:",
|
||||
"about.notice": "A Mattermost rendszert a <linkServer>szerver</linkServer>, <linkDesktop>asztali</linkDesktop> és <linkMobile>mobilalkalmazásainkban</linkMobile> használt nyílt forráskódú szoftverek teszik lehetővé.",
|
||||
"about.privacy": "Adatvédelmi irányelvek",
|
||||
@@ -2544,7 +2543,6 @@
|
||||
"app.channel.post_update_channel_purpose_message.removed": "{username} eltávolította a csatorna célját (volt: {old})",
|
||||
"app.channel.post_update_channel_purpose_message.updated_from": "{username} frissítette a csatorna célját: {old} -> {new}",
|
||||
"app.channel.post_update_channel_purpose_message.updated_to": "{username} frissítette a csatorna célját erre: {new}",
|
||||
"app.plugin.marketplace_plugins.app_error": "Hiba történt a piactér szerveréhez való csatlakozáskor. Kérjük, ellenőrizze a beállításait a [Rendszerkonzolban]({siteURL}/admin_console/plugins/plugin_management).",
|
||||
"apps.error": "Hiba: {error}",
|
||||
"apps.error.command.field_missing": "Hiányzó kötelező mezők: `{fieldName}`.",
|
||||
"apps.error.command.same_channel": "Beszélgetés ismételve lett a mezőhöz `{fieldName}`: `{option}`.",
|
||||
@@ -3846,7 +3844,6 @@
|
||||
"login_mfa.token": "Többtényezős hitelesítés (MFA) token",
|
||||
"manage_channel_groups_modal.search_placeholder": "Csoportok keresése",
|
||||
"manage_team_groups_modal.search_placeholder": "Csoportok keresése",
|
||||
"marketplace_list.count_total_page": "{startCount, number} - {endCount, number} bővítmény / {total, number} összesen",
|
||||
"marketplace_modal.install_plugins": "Bővítmények telepítése",
|
||||
"marketplace_modal.installing": "Telepítés...",
|
||||
"marketplace_modal.list.configure": "Beállítás",
|
||||
@@ -4576,11 +4573,7 @@
|
||||
"start_trial.modal.gettingTrial": "Próbaidőszak kezdése...",
|
||||
"start_trial.modal.loaded": "Betöltve!",
|
||||
"start_trial.modal.loading": "Betöltés...",
|
||||
"start_trial.modal_body": "Férjen hozzá az összes platform szolgáltatáshoz beleértve a fejlett biztonságot és a vállalati megfelelősséget.",
|
||||
"start_trial.modal_btn.nottnow": "Ne most",
|
||||
"start_trial.modal_btn.start": "30 napos ingyenes próbaidőszak indítása",
|
||||
"start_trial.modal_btn.start_free_trial": "30 napos ingyenes próbaidőszak indítása",
|
||||
"start_trial.modal_title": "Az ingyenes Enterprise kiadás próbaidőszak indítása",
|
||||
"start_trial.tutorialTip.desc": "Fedezze fel a legkeresettebb prémium funkcióinkat. Határozza meg a felhasználók hozzáférését a vendégfiókok segítségével, automatizálja a megfelelőségi jelentéseket, és küldjön biztonságos, csak azonosítóval ellátott mobil push-értesítéseket.",
|
||||
"start_trial.tutorialTip.title": "Próbálja ki prémium szolgáltatásainkat ingyen",
|
||||
"status_dropdown.dnd_sub_menu_header": "Értesítések kikapcsolása eddig:",
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
"about.enterpriseEditione1": "Edizione Enterprise",
|
||||
"about.hash": "Hash di compilazione:",
|
||||
"about.hashee": "Hash di compilazione EE:",
|
||||
"about.hashwebapp": "Webapp Build Hash:",
|
||||
"about.licensed": "Licenza concessa a:",
|
||||
"about.notice": "Mattermost è reso possibile dal software open source utilizzato sul nostro <linkServer>server</linkServer>, <linkDesktop>desktop</linkDesktop> e <linkMobile>mobile</linkMobile> apps.",
|
||||
"about.privacy": "Politica sulla privacy",
|
||||
@@ -1970,7 +1969,6 @@
|
||||
"app.channel.post_update_channel_purpose_message.removed": "{username} ha cancellato lo scopo del canale (era: {old})",
|
||||
"app.channel.post_update_channel_purpose_message.updated_from": "{username} ha aggiornato lo scopo del canale da: {old} a: {new}",
|
||||
"app.channel.post_update_channel_purpose_message.updated_to": "{username} ha aggiornato lo scopo del canale a: {new}",
|
||||
"app.plugin.marketplace_plugins.app_error": "Errore connessione al server marketplace. Controllare le impostazioni in [Console di Sistema]({siteURL}/admin_console/plugins/plugin_management).",
|
||||
"apps.error": "Errore: {error}",
|
||||
"apps.error.command.field_missing": "Campo richiesto mancante: `{fieldName}`.",
|
||||
"apps.error.command.same_channel": "Canale ripetuto per il campo `{fieldName}`: `{option}`.",
|
||||
@@ -2932,7 +2930,6 @@
|
||||
"login_mfa.token": "Token MFA",
|
||||
"manage_channel_groups_modal.search_placeholder": "Cerca gruppi",
|
||||
"manage_team_groups_modal.search_placeholder": "Cerca gruppi",
|
||||
"marketplace_list.count_total_page": "{startCount, number} - {endCount, number} {total, plural, one {plugin} other {plugins}} di {total, number} totali",
|
||||
"marketplace_modal.install_plugins": "Installa Plugin",
|
||||
"marketplace_modal.installing": "Installazione...",
|
||||
"marketplace_modal.list.configure": "Configura",
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
"about.enterpriseEditione1": "Enterprise Edition",
|
||||
"about.hash": "ビルドハッシュ値:",
|
||||
"about.hashee": "EEビルドハッシュ値:",
|
||||
"about.hashwebapp": "Webアプリのビルドハッシュ:",
|
||||
"about.licensed": "ライセンス供給先:",
|
||||
"about.notice": "Mattermostは<linkServer>server</linkServer>、 <linkDesktop>desktop</linkDesktop>、<linkMobile>mobile</linkMobile>で使用されているオープンソースソフトウェアによって実現されています。",
|
||||
"about.privacy": "プライバシーポリシー",
|
||||
@@ -2648,7 +2647,6 @@
|
||||
"app.channel.post_update_channel_purpose_message.removed": "{username} がチャンネルの目的({old})を削除しました",
|
||||
"app.channel.post_update_channel_purpose_message.updated_from": "{username} がチャンネルの目的を {old} から {new} へ更新しました",
|
||||
"app.channel.post_update_channel_purpose_message.updated_to": "{username} がチャンネルの目的を {new} へ更新しました",
|
||||
"app.plugin.marketplace_plugins.app_error": "マーケットプレースサーバーへ接続する際にエラーが発生しました。[システムコンソール]({siteURL}/admin_console/plugins/plugin_management)内の設定を確認してください。",
|
||||
"apps.error": "エラー: {error}",
|
||||
"apps.error.command.field_missing": "必須フィールドが存在しません: `{fieldName}`。",
|
||||
"apps.error.command.same_channel": "フィールド `{fieldName}` でチャンネルが繰り返し指定されました: `{option}`。",
|
||||
@@ -4081,7 +4079,6 @@
|
||||
"mark_all_threads_as_read_modal.title": "すべてのスレッドを既読にしますか?",
|
||||
"marketplace_command.disabled": "マーケットプレイスが無効になっています。詳しくは、システム管理者に問い合わせてください。",
|
||||
"marketplace_command.no_permission": "マーケットプレイスにアクセスするための適切な権限を持っていません。",
|
||||
"marketplace_list.count_total_page": "{startCount, number} - {endCount, number} 全 {total, number} プラグイン中",
|
||||
"marketplace_modal.install_plugins": "プラグインをインストールする",
|
||||
"marketplace_modal.installing": "インストールしています...",
|
||||
"marketplace_modal.list.configure": "設定",
|
||||
@@ -4156,22 +4153,10 @@
|
||||
"modal.manual_status.title_offline": "ステータスが \"オフライン\" になりました",
|
||||
"modal.manual_status.title_ooo": "ステータスが \"外出中\" になりました",
|
||||
"more.details": "もっと詳しく",
|
||||
"more_channels.channel_purpose": "チャンネル情報: メンバーシップ状況: 加入済, メンバー数 {memberCount} , 目的: {channelPurpose}",
|
||||
"more_channels.count": "{count}件",
|
||||
"more_channels.count_one": "1件",
|
||||
"more_channels.count_zero": "0件",
|
||||
"more_channels.create": "チャンネルを作成する",
|
||||
"more_channels.hide_joined": "参加したことを表示しない",
|
||||
"more_channels.hide_joined_checked": "チャンネルに参加したことを表示しないチェックボックスがチェック済です",
|
||||
"more_channels.hide_joined_not_checked": "チャンネルに参加したことを表示しないチェックボックスがチェックされていません",
|
||||
"more_channels.joined": "参加済",
|
||||
"more_channels.membership_indicator": "メンバーシップ状況: 参加済",
|
||||
"more_channels.next": "次へ",
|
||||
"more_channels.noArchived": "アーカイブされたチャンネルはありません",
|
||||
"more_channels.noMore": "\"{text}\"の結果はありません",
|
||||
"more_channels.noPublic": "公開チャンネルはありません",
|
||||
"more_channels.prev": "前へ",
|
||||
"more_channels.searchError": "違うキーワードで検索してみたり、入力ミスを確認したり、フィルター設定を変更して再度お試しください。",
|
||||
"more_channels.show_archived_channels": "表示: アーカイブチャンネル",
|
||||
"more_channels.show_public_channels": "表示: 公開チャンネル",
|
||||
"more_channels.title": "他のチャンネル",
|
||||
@@ -4979,11 +4964,7 @@
|
||||
"start_trial.modal.gettingTrial": "トライアル開始中...",
|
||||
"start_trial.modal.loaded": "読み込みが完了しました!",
|
||||
"start_trial.modal.loading": "読み込み中です...",
|
||||
"start_trial.modal_body": "高度なセキュリティとコンプライアンス機能を含むプラットフォームのすべての機能にアクセスできます。",
|
||||
"start_trial.modal_btn.nottnow": "今はしない",
|
||||
"start_trial.modal_btn.start": "30日間の無料トライアルを開始する",
|
||||
"start_trial.modal_btn.start_free_trial": "30日間の無料トライアルを開始する",
|
||||
"start_trial.modal_title": "今すぐEnterprise版の無料トライアルを開始する",
|
||||
"start_trial.tutorialTip.desc": "最も要望の多いプレミアム機能を紹介します。ゲストアカウント、コンプライアンスレポートの自動化、IDのみを利用したセキュアなモバイルプッシュ通知。",
|
||||
"start_trial.tutorialTip.title": "プレミアム機能を無料でお試しいただけます",
|
||||
"status_dropdown.dnd_sub_menu_header": "通知を無効にする期間:",
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
"about.enterpriseEditione1": "엔터프라이즈 에디션",
|
||||
"about.hash": "빌드 해쉬:",
|
||||
"about.hashee": "EE 빌드 해쉬:",
|
||||
"about.hashwebapp": "Webapp 빌드 해쉬:",
|
||||
"about.licensed": "다음 사용자에게 허가되었습니다:",
|
||||
"about.notice": "Mattermost는 <linkServer>서버</linkServer>, <linkDesktop>데스크톱</linkDesktop> 그리고 <linkMobile>모바일</linkMobile>에서 오픈소스 소프트웨어로 이용 가능합니다.",
|
||||
"about.privacy": "개인정보처리방침",
|
||||
@@ -2834,7 +2833,6 @@
|
||||
"login_mfa.token": "MFA 토큰",
|
||||
"manage_channel_groups_modal.search_placeholder": "그룹 찾기",
|
||||
"manage_team_groups_modal.search_placeholder": "그룹 찾기",
|
||||
"marketplace_list.count_total_page": "{startCount, number} - {endCount, number} {total, plural, one {plugin} other {plugins}} of {total, number} total",
|
||||
"marketplace_modal.install_plugins": "설치된 플러그인",
|
||||
"marketplace_modal.installing": "설치중...",
|
||||
"marketplace_modal.list.configure": "설정",
|
||||
@@ -3173,7 +3171,6 @@
|
||||
"signup_user_completed.validEmail": "유효한 전자우편 주소를 입력해주세요",
|
||||
"someting.string": "기본문자열",
|
||||
"start_trial.modal.loading": "로딩중...",
|
||||
"start_trial.modal_btn.start": "30일 평가판 시작",
|
||||
"status_dropdown.menuAriaLabel": "set status",
|
||||
"status_dropdown.set_away": "다른 용무 중",
|
||||
"status_dropdown.set_dnd": "방해 금지",
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
"about.enterpriseEditione1": "Enterprise-Editie",
|
||||
"about.hash": "Compilatiehash:",
|
||||
"about.hashee": "EE Compilatiehash:",
|
||||
"about.hashwebapp": "Webapp Build Hash:",
|
||||
"about.licensed": "Licentie verleend aan:",
|
||||
"about.notice": "Mattermost wordt mogelijk gemaakt door de open source software die wordt gebruikt in onze apps <linkServer>server</linkServer>, <linkDesktop>desktop</linkDesktop> en <linkMobile>mobiel</linkMobile> apps.",
|
||||
"about.privacy": "Privacybeleid",
|
||||
@@ -2646,7 +2645,6 @@
|
||||
"app.channel.post_update_channel_purpose_message.removed": "{username} verwijderde het kanaal doel (was: {old})",
|
||||
"app.channel.post_update_channel_purpose_message.updated_from": "{username} heeft het doel van het kanaal bijgewerkt van: {old} naar: {new}",
|
||||
"app.channel.post_update_channel_purpose_message.updated_to": "{username} heeft het kanaaldoel bijgewerkt naar: {new}",
|
||||
"app.plugin.marketplace_plugins.app_error": "Fout bij het maken van verbinding met de marktplaats server. Controleer uw instellingen in de [Systeemconsole] ({siteURL}/admin_console/plugins/plugin_management).",
|
||||
"apps.error": "Fout: {error}",
|
||||
"apps.error.command.field_missing": "Verplichte velden ontbreken: `{fieldName}`.",
|
||||
"apps.error.command.same_channel": "Kanaal herhaald voor veld `{fieldName}`: `{option}`.",
|
||||
@@ -4079,7 +4077,6 @@
|
||||
"mark_all_threads_as_read_modal.title": "Al jouw draadjes als gelezen markeren?",
|
||||
"marketplace_command.disabled": "De marktplaats is uitgeschakeld. Neem contact op met jouw systeembeheerder voor meer informatie.",
|
||||
"marketplace_command.no_permission": "Je hebt niet de juiste rechten om toegang te krijgen tot de marktplaats.",
|
||||
"marketplace_list.count_total_page": "{startCount, number} - {endCount, number} {total, plural, one {plugin} other {plugins}} van {total, number} in het totaal",
|
||||
"marketplace_modal.install_plugins": "Installeer Plugins",
|
||||
"marketplace_modal.installing": "Bezig met Installeren...",
|
||||
"marketplace_modal.list.configure": "Configureren",
|
||||
@@ -4964,11 +4961,7 @@
|
||||
"start_trial.modal.gettingTrial": "Proefversie ophalen...",
|
||||
"start_trial.modal.loaded": "Geladen!",
|
||||
"start_trial.modal.loading": "Laden...",
|
||||
"start_trial.modal_body": "Toegang tot alle platformfuncties, inclusief geavanceerde beveiliging en de nalevingsregels met de onderneming.",
|
||||
"start_trial.modal_btn.nottnow": "Niet nu",
|
||||
"start_trial.modal_btn.start": "Start gratis je 30-dagen proefperiode",
|
||||
"start_trial.modal_btn.start_free_trial": "Start je gratis 30-dagen proefperiode",
|
||||
"start_trial.modal_title": "Start nu je gratis Enterprise-proefperiode",
|
||||
"start_trial.tutorialTip.desc": "Ontdek onze meest gevraagde premium-functies. Bepaal gebruikerstoegang met gastaccounts, automatiseer nalevingsrapporten en verstuur veilige mobiele pushmeldingen met alleen ID's.",
|
||||
"start_trial.tutorialTip.title": "Probeer onze premium-functies gratis",
|
||||
"status_dropdown.dnd_sub_menu_header": "Meldingen uitschakelen tot:",
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
"about.enterpriseEditione1": "Edycja Enterprise",
|
||||
"about.hash": "Hash Kompilacji:",
|
||||
"about.hashee": "Hash Kompilacji Enterprise:",
|
||||
"about.hashwebapp": "Hash Kompilacji Aplikacji Webowej:",
|
||||
"about.licensed": "Licencjonowany dla:",
|
||||
"about.notice": "Mattermost jest został stworzony dzięki oprogramowaniu open source użytym na naszym <linkServer>server</linkServer>, <linkDesktop>desktop</linkDesktop> i <linkMobile>mobile</linkMobile> aplikacje.",
|
||||
"about.privacy": "Polityka prywatności",
|
||||
@@ -1340,6 +1339,7 @@
|
||||
"admin.license.title": "Edycja i licencja",
|
||||
"admin.license.trial-request.accept-terms": "Klikając <strong>Rozpocznij wersję próbną</strong>, wyrażam zgodę na <linkEvaluation>Mattermost Software and Services License Agreement</linkEvaluation>, <linkPrivacy>Privacy Policy</linkPrivacy> oraz na otrzymywanie wiadomości e-mail dotyczących produktu.",
|
||||
"admin.license.trial-request.embargoed": "Nie mogliśmy przetworzyć tego żądania z powodu ograniczeń dotyczących krajów objętych embargiem. <link> Dowiedz się więcej w naszej dokumentacji</link> lub wyślij wiadomość na adres legal@mattermost.com, aby uzyskać odpowiedzi na pytania dotyczące ograniczeń eksportowych.",
|
||||
"admin.license.trial-request.embargoed.button": "Zamknij",
|
||||
"admin.license.trial-request.startTrial": "Rozpoczęcie wersji trial",
|
||||
"admin.license.trial-request.title": "Korzystaj z Mattermost Enterprise Edition za darmo przez następne 30 dni. Nie ma obowiązku zakupu lub karty kredytowej. ",
|
||||
"admin.license.trialCard.contactSales": "Kontakt ze sprzedażą",
|
||||
@@ -2563,6 +2563,9 @@
|
||||
"admin.webserverModeUncompressed": "Nieskompresowany",
|
||||
"admin.webserverModeUncompressedDescription": "Serwer Mattermost będzie serwował nieskompresowane statyczne pliki.",
|
||||
"admin_settings.save_unsaved_changes": "Należy najpierw zapisać niezapisane zmiany",
|
||||
"air_gapped_modal.close": "Zamknij",
|
||||
"air_gapped_modal.description": "Aby uruchomić wersję próbną, odwiedź stronę {link} i poproś o klucz próbny.",
|
||||
"air_gapped_modal.title": "Poproś o klucz próbny",
|
||||
"alert_banner.tooltipCloseBtn": "Zamknij",
|
||||
"analytics.chart.loading": "Ładowanie...",
|
||||
"analytics.chart.meaningful": "Za mało danych dla sensownej reprezentacji.",
|
||||
@@ -2648,7 +2651,7 @@
|
||||
"app.channel.post_update_channel_purpose_message.removed": "{username} usunął cel kanału (było: {old})",
|
||||
"app.channel.post_update_channel_purpose_message.updated_from": "{username} zaktualizował cel kanału z: {old} na: {new}",
|
||||
"app.channel.post_update_channel_purpose_message.updated_to": "{username} zaktualizował cel kanału na: {new}",
|
||||
"app.plugin.marketplace_plugins.app_error": "Błąd podczas łączenia z serwerem marketplace. Sprawdź ustawienia w [Konsoli systemowej] ({siteURL}/admin_console/plugins/plugin_management).",
|
||||
"app_bar.marketplace": "Sklep z Aplikacjami",
|
||||
"apps.error": "Błąd: {error}",
|
||||
"apps.error.command.field_missing": "Brakujące wymagane pola: `{fieldName}`.",
|
||||
"apps.error.command.same_channel": "Kanał powtórzony dla pola `{fieldName}`: `{option}`.",
|
||||
@@ -3437,6 +3440,9 @@
|
||||
"filtered_user_list.userStatus": "Status użytkownika:",
|
||||
"flag_post.flag": "Oznacz do obserwacji",
|
||||
"flag_post.unflag": "Usuń flagę",
|
||||
"footer_pagination.count": "Pokazuje{startCount, number}-{endCount, number} z {total, number}",
|
||||
"footer_pagination.next": "Dalej",
|
||||
"footer_pagination.prev": "Wstecz",
|
||||
"forward_post_button.label": "Przekaż",
|
||||
"forward_post_modal.button.cancel": "Anuluj",
|
||||
"forward_post_modal.button.forward": "Przekaż",
|
||||
@@ -4081,8 +4087,8 @@
|
||||
"mark_all_threads_as_read_modal.title": "Oznaczyć wszystkie twoje wątki jako przeczytane?",
|
||||
"marketplace_command.disabled": "Sklep jest wyłączony. W celu uzyskania szczegółowych informacji należy skontaktować się z administratorem systemu.",
|
||||
"marketplace_command.no_permission": "Nie masz odpowiednich uprawnień, aby uzyskać dostęp do sklepu.",
|
||||
"marketplace_list.count_total_page": "{startCount, number} - {endCount, number} {total, plural, one {wtyczka} other {wtyczki}} z {total, number} wszystkich",
|
||||
"marketplace_modal.install_plugins": "Zainstaluj wtyczki",
|
||||
"marketplace_modal.app_error": "Błąd połączenia z serwerem marketplace. Proszę sprawdzić swoje ustawienia w <linkConsole>Konsoli Systemowej </linkConsole> .",
|
||||
"marketplace_modal.install_plugins": "Zainstaluj Wtyczki",
|
||||
"marketplace_modal.installing": "Instalowanie...",
|
||||
"marketplace_modal.list.configure": "Konfiguruj",
|
||||
"marketplace_modal.list.configure.plugin": "Skonfiguruj {plugin}",
|
||||
@@ -4099,12 +4105,13 @@
|
||||
"marketplace_modal.list.update_confirmation.message.warning_major_version": "Ta aktualizacja może zawierać duże zmiany.",
|
||||
"marketplace_modal.list.update_confirmation.message.warning_major_version_with_release_notes": "Ta aktualizacja może zawierać duże zmiany. Przejrzyj [release notes](!{releaseNotesUrl}) przed aktualizacją.",
|
||||
"marketplace_modal.list.update_confirmation.title": "Potwierdź aktualizację wtyczki",
|
||||
"marketplace_modal.no_plugins": "Obecnie nie ma dostępnych wtyczek.",
|
||||
"marketplace_modal.no_plugins": "Nie znaleziono żadnych wtyczek",
|
||||
"marketplace_modal.no_plugins_installed": "Nie masz zainstalowanych żadnych wtyczek.",
|
||||
"marketplace_modal.search": "Szukaj w Sklepie",
|
||||
"marketplace_modal.search": "Szukaj w sklepie",
|
||||
"marketplace_modal.tabs.all_listing": "Wszystko",
|
||||
"marketplace_modal.tabs.installed_listing": "Zainstalowane",
|
||||
"marketplace_modal.title": "Sklep",
|
||||
"marketplace_modal.tabs.installed_listing": "Zainstalowane ({count})",
|
||||
"marketplace_modal.title": "Sklep z Aplikacjami",
|
||||
"marketplace_modal_list.no_plugins_filter": "Brak wyników dla \"{filter}\"",
|
||||
"members_popover.button.message": "wiadomość",
|
||||
"menu.cloudFree.enterpriseTrialDescription": "Twój okres próbny jest aktywny do {trialEndDay}. Poznaj nasze najważniejsze funkcje Enterprise. <openModalLink> Dowiedz się więcej</openModalLink>",
|
||||
"menu.cloudFree.enterpriseTrialTitle": "Enterprise Trial",
|
||||
@@ -4156,22 +4163,13 @@
|
||||
"modal.manual_status.title_offline": "Twój status został ustawiony na \"Offline\"",
|
||||
"modal.manual_status.title_ooo": "Twój status został ustawiony na \"Poza biurem\"",
|
||||
"more.details": "Więcej informacji",
|
||||
"more_channels.channel_purpose": "Informacje o kanale: Wskaźnik członkostwa: Dołączyło, liczba członków {memberCount}, Propozycje: {channelPurpose}",
|
||||
"more_channels.count": "{count} Wyników",
|
||||
"more_channels.count_one": "1 Wynik",
|
||||
"more_channels.count_zero": "0 Wyników",
|
||||
"more_channels.create": "Stwórz kanał",
|
||||
"more_channels.hide_joined": "Ukryj dołączonych",
|
||||
"more_channels.hide_joined_checked": "Pole wyboru Ukryj połączone kanały, zaznaczone",
|
||||
"more_channels.hide_joined_not_checked": "Pole wyboru Ukryj połączone kanały, nie zaznaczone",
|
||||
"more_channels.joined": "Dołączył",
|
||||
"more_channels.membership_indicator": "Wskaźnik członkostwa: Dołączył",
|
||||
"more_channels.createClick": "Kliknij przycisk 'Utwórz nowy kanał', aby dodać nowy",
|
||||
"more_channels.join": "Dołącz",
|
||||
"more_channels.joining": "Dołączanie...",
|
||||
"more_channels.next": "Dalej",
|
||||
"more_channels.noArchived": "Brak zarchiwizowanych kanałów",
|
||||
"more_channels.noMore": "Brak wyników dla \"{text}\"",
|
||||
"more_channels.noPublic": "Brak kanałów publicznych",
|
||||
"more_channels.prev": "Wstecz",
|
||||
"more_channels.searchError": "Spróbuj wyszukać inne słowa kluczowe, sprawdzić literówki lub dostosować filtry.",
|
||||
"more_channels.show_archived_channels": "Pokaż: Archiwizowane kanały",
|
||||
"more_channels.show_public_channels": "Pokaż: Publiczne kanały",
|
||||
"more_channels.title": "Więcej Kanałów",
|
||||
@@ -4236,7 +4234,7 @@
|
||||
"navbar_dropdown.logout": "Wylogowanie",
|
||||
"navbar_dropdown.manageGroups": "Zarządzaj grupami",
|
||||
"navbar_dropdown.manageMembers": "Zarządzaj użytkownikami",
|
||||
"navbar_dropdown.marketplace": "Sklep",
|
||||
"navbar_dropdown.marketplace": "Sklep z Aplikacjami",
|
||||
"navbar_dropdown.menuAriaLabel": "menu główne",
|
||||
"navbar_dropdown.nativeApps": "Pobierz aplikacje",
|
||||
"navbar_dropdown.profileSettings": "Profil",
|
||||
@@ -4294,9 +4292,10 @@
|
||||
"onboardingTask.checklist.downloads": "Teraz, gdy wszystko jest już gotowe, <link>pobierz nasze aplikacje</link>",
|
||||
"onboardingTask.checklist.higher_security_features": "Interesują Cię nasze funkcje o podwyższonym poziomie bezpieczeństwa?",
|
||||
"onboardingTask.checklist.main_subtitle": "Zaczynamy i działamy.",
|
||||
"onboardingTask.checklist.no_thanks": "Nie, dzięki",
|
||||
"onboardingTask.checklist.start_enterprise_now": "Rozpocznij bezpłatny okres próbny Enterprise już teraz!",
|
||||
"onboardingTask.checklist.task_complete_your_profile": "Uzupełnij swój profil.",
|
||||
"onboardingTask.checklist.task_create_from_work_template": "Utwórz z szablonu - ustaw kanał z połączonymi tablicami i playbookami.",
|
||||
"onboardingTask.checklist.task_create_from_work_template": "Utwórz z szablonu",
|
||||
"onboardingTask.checklist.task_download_mm_apps": "Pobierz aplikację desktopową i mobilną.",
|
||||
"onboardingTask.checklist.task_explore_other_tools_in_platform": "Poznaj inne narzędzia w platformie.",
|
||||
"onboardingTask.checklist.task_invite_team_members": "Zaproś członków zespołu do obszaru roboczego.",
|
||||
@@ -4868,7 +4867,7 @@
|
||||
"sidebar.directchannel.you": "{displayname} (ty)",
|
||||
"sidebar.menu.item.notSelected": "nie wybrany",
|
||||
"sidebar.menu.item.selected": "wybrane",
|
||||
"sidebar.openDirectMessage": "Otwórz Wiadomość Bezpośrednią",
|
||||
"sidebar.openDirectMessage": "Otwórz wiadomość bezpośrednią",
|
||||
"sidebar.show": "Pokaż",
|
||||
"sidebar.sort": "Sortuj",
|
||||
"sidebar.sortedByRecencyLabel": "Ostatnia aktywność",
|
||||
@@ -4883,15 +4882,17 @@
|
||||
"sidebar.types.favorites": "ULUBIONE",
|
||||
"sidebar.types.unreads": "NIEPRZECZYTANE",
|
||||
"sidebar.unreads": "Więcej nieprzeczytanych",
|
||||
"sidebar_left.add_channel_dropdown.browseChannels": "Przeglądaj Kanały",
|
||||
"sidebar_left.addChannelsCta": "Dodaj kanały",
|
||||
"sidebar_left.add_channel_cta_dropdown.dropdownAriaLabel": "Dodaj Menu Rozwijane Kanału",
|
||||
"sidebar_left.add_channel_dropdown.browseChannels": "Przeglądaj kanały",
|
||||
"sidebar_left.add_channel_dropdown.browseOrCreateChannels": "Przeglądaj lub twórz kanały",
|
||||
"sidebar_left.add_channel_dropdown.createCategory": "Utwórz nową kategorię",
|
||||
"sidebar_left.add_channel_dropdown.createNewChannel": "Utwórz Nowy Kanał",
|
||||
"sidebar_left.add_channel_dropdown.createNewChannel": "Utwórz nowy kanał",
|
||||
"sidebar_left.add_channel_dropdown.dropdownAriaLabel": "Dodaj Menu Rozwijane Kanału",
|
||||
"sidebar_left.add_channel_dropdown.invitePeople": "Zaproś Ludzi",
|
||||
"sidebar_left.add_channel_dropdown.invitePeople": "Zaproś osoby",
|
||||
"sidebar_left.add_channel_dropdown.invitePeopleExtraText": "Dodaj ludzi do zespołu",
|
||||
"sidebar_left.add_channel_dropdown.work_template": "Utwórz z szablonu",
|
||||
"sidebar_left.add_channel_dropdown.work_template_extra": "Skonfiguruj kanał z połączonymi tablicami i playbookami",
|
||||
"sidebar_left.add_channel_dropdown.work_template_extra": "Połącz razem kanały, tablice i playbooki",
|
||||
"sidebar_left.channel_filter.filterByUnread": "Filtruj według nieprzeczytanych",
|
||||
"sidebar_left.channel_filter.filterUnreadAria": "filtr nieprzeczytanych",
|
||||
"sidebar_left.channel_filter.showAllChannels": "Pokaż wszystkie kanały",
|
||||
@@ -4931,6 +4932,7 @@
|
||||
"sidebar_left.sidebar_channel_menu.unfavoriteChannel": "Cofnij ulubione",
|
||||
"sidebar_left.sidebar_channel_menu.unmuteChannel": "Wyłącz Wyciszenie Kanału",
|
||||
"sidebar_left.sidebar_channel_menu.unmuteConversation": "Wyłącz Wyciszenie Rozmowy",
|
||||
"sidebar_left.sidebar_channel_navigator.addChannelsCta": "Dodaj kanały",
|
||||
"sidebar_left.sidebar_channel_navigator.inviteUsers": "Zaproś Użytkowników",
|
||||
"sidebar_right_menu.console": "Konsola systemu",
|
||||
"sidebar_right_menu.flagged": "Oznaczone Wiadmości",
|
||||
@@ -4979,11 +4981,7 @@
|
||||
"start_trial.modal.gettingTrial": "Pobieranie wersji Testowej...",
|
||||
"start_trial.modal.loaded": "Załadowany!",
|
||||
"start_trial.modal.loading": "Ładowanie...",
|
||||
"start_trial.modal_body": "Uzyskaj dostęp do wszystkich funkcji platformy, w tym zaawansowanych zabezpieczeń i zgodności korporacyjnej.",
|
||||
"start_trial.modal_btn.nottnow": "Nie teraz",
|
||||
"start_trial.modal_btn.start": "Rozpocznij bezpłatny 30-dniowy okres próbny",
|
||||
"start_trial.modal_btn.start_free_trial": "Rozpocznij bezpłatny 30-dniowy okres próbny",
|
||||
"start_trial.modal_title": "Rozpocznij bezpłatny okres próbny Enterprise już teraz",
|
||||
"start_trial.tutorialTip.desc": "Zapoznaj się z naszymi najbardziej pożądanymi funkcjami premium. Określ dostęp użytkowników za pomocą kont gości, zautomatyzuj raporty zgodności i wysyłaj bezpieczne powiadomienia mobilne push z wykorzystaniem wyłącznie identyfikatorów.",
|
||||
"start_trial.tutorialTip.title": "Wypróbuj nasze funkcje premium za darmo",
|
||||
"status_dropdown.dnd_sub_menu_header": "Wyłącz powiadomienia do:",
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
"about.enterpriseEditione1": "Enterprise Edition",
|
||||
"about.hash": "Hash de Compilação:",
|
||||
"about.hashee": "Hash de Compilação EE:",
|
||||
"about.hashwebapp": "Webapp Build Hash:",
|
||||
"about.licensed": "Licenciado para:",
|
||||
"about.notice": "Mattermost é possível graças ao software de código aberto usado em nosso <linkServer>servidor</linkServer>, <linkDesktop>desktop</linkDesktop> e aplicativos <linkMobile>móveis</linkMobile>.",
|
||||
"about.privacy": "Política de Privacidade",
|
||||
@@ -26,6 +25,8 @@
|
||||
"accessibility.button.Info": "Informações",
|
||||
"accessibility.button.Search": "Procurar",
|
||||
"accessibility.button.attachment": "anexo",
|
||||
"accessibility.button.bold": "negrito",
|
||||
"accessibility.button.code": "código",
|
||||
"accessibility.button.dialog": "Diálogo {dialogName}",
|
||||
"accessibility.button.italic": "itálico",
|
||||
"accessibility.button.numbered_list": "lista numerada",
|
||||
@@ -248,6 +249,7 @@
|
||||
"admin.billing.history.fractionalAndRatedUsers": "{fractionalUsers} usuários medidos, {fullUsers} usuários com taxa total, {partialUsers} usuários com cobranças parciais",
|
||||
"admin.billing.history.fractionalUsers": "{fractionalUsers} usuários",
|
||||
"admin.billing.history.noBillingHistory": "No futuro, é aqui que seu histórico de faturamento será exibido.",
|
||||
"admin.billing.history.onPremUsers": "{num} usuários",
|
||||
"admin.billing.history.pageInfo": "{startRecord} - {endRecord} de {totalRecords}",
|
||||
"admin.billing.history.paid": "Pago",
|
||||
"admin.billing.history.paymentFailed": "Pagamento falhou",
|
||||
@@ -281,6 +283,7 @@
|
||||
"admin.billing.subscription.cancelSubscriptionSection.contactUs": "Contate-Nos",
|
||||
"admin.billing.subscription.cancelSubscriptionSection.description": "No momento, a exclusão de um espaço de trabalho só pode ser feita com a ajuda de um representante de suporte ao cliente.",
|
||||
"admin.billing.subscription.cancelSubscriptionSection.title": "Cancelar sua assinatura",
|
||||
"admin.billing.subscription.cloudMonthlyBadge": "Mensalmente",
|
||||
"admin.billing.subscription.creditCardExpired": "Seu cartão de crédito expirou. Atualize suas informações de pagamento para evitar interrupções.",
|
||||
"admin.billing.subscription.creditCardHasExpired": "Seu cartão de crédito expirou",
|
||||
"admin.billing.subscription.goBackTryAgain": "Volte e tente novamente",
|
||||
@@ -2192,7 +2195,6 @@
|
||||
"app.channel.post_update_channel_purpose_message.removed": "{username} removeu o propósito do canal (era: {old})",
|
||||
"app.channel.post_update_channel_purpose_message.updated_from": "{username} atualizou o propósito do canal de: {old} para: {new}",
|
||||
"app.channel.post_update_channel_purpose_message.updated_to": "{username} atualizou o propósito do canal para: {new}",
|
||||
"app.plugin.marketplace_plugins.app_error": "Erro ao conectar-se ao servidor do marketplace. Por favor, verifique suas configurações no [Console do Sistema]({siteURL}/admin_console/plugins/plugin_management).",
|
||||
"apps.error": "Erro: {error}",
|
||||
"apps.error.command.same_channel": "Canal repetido para o campo `{fieldName}`: `{option}`.",
|
||||
"apps.error.command.same_option": "Opção repetida para o campo `{fieldName}`: `{option}`.",
|
||||
@@ -3151,7 +3153,6 @@
|
||||
"login_mfa.token": "Token MFA",
|
||||
"manage_channel_groups_modal.search_placeholder": "Buscar grupos",
|
||||
"manage_team_groups_modal.search_placeholder": "Buscar grupos",
|
||||
"marketplace_list.count_total_page": "{startCount, number} - {endCount, number} {total, plural, one {plugin} other {plugins}} de {total, number} total",
|
||||
"marketplace_modal.install_plugins": "Instalar Plugins",
|
||||
"marketplace_modal.installing": "Instalando...",
|
||||
"marketplace_modal.list.configure": "Configurar",
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
"about.enterpriseEditione1": "Ediția Enterprise",
|
||||
"about.hash": "Construcția Hash:",
|
||||
"about.hashee": "EE Construcția Hash:",
|
||||
"about.hashwebapp": "Webapp Construcția Hash:",
|
||||
"about.licensed": "Licențiat la:",
|
||||
"about.notice": "Mattermost este făcut posibil de către software-ul open source utilizat în <linkServer>server</linkServer>, <linkDesktop>desktop</linkDesktop> și <linkMobile>mobil</linkMobile> aplicații.",
|
||||
"about.privacy": "Politica de confidentialitate",
|
||||
@@ -2254,7 +2253,6 @@
|
||||
"app.channel.post_update_channel_purpose_message.removed": "{username} a eliminat scopul canalului (a fost: {old})",
|
||||
"app.channel.post_update_channel_purpose_message.updated_from": "{username} a actualizat scopul canalului de la: {old} la: {new}",
|
||||
"app.channel.post_update_channel_purpose_message.updated_to": "{username} a actualizat scopul canalului la: {new}",
|
||||
"app.plugin.marketplace_plugins.app_error": "Eroare la conectarea la serverul de pe piață. Verificați setările din [Consola de sistem]({siteURL}/admin_console/plugins/plugin_management).",
|
||||
"apps.error": "Eroare: {error}",
|
||||
"apps.error.command.field_missing": "Câmpurile obligatorii lipsesc: `{fieldName}`.",
|
||||
"apps.error.command.unknown_channel": "Canal necunoscut pentru câmpul `{fieldName}`: `{option}`.",
|
||||
@@ -3237,7 +3235,6 @@
|
||||
"login_mfa.token": "Eroare încearcă să se autentifice Mae token",
|
||||
"manage_channel_groups_modal.search_placeholder": "Căutați grupuri",
|
||||
"manage_team_groups_modal.search_placeholder": "Căutați grupuri",
|
||||
"marketplace_list.count_total_page": "{startCount, number} - {endCount, number} {total, plural, one {plugin} other {pluginuri}} din {total, number} total",
|
||||
"marketplace_modal.install_plugins": "Instalați pluginuri",
|
||||
"marketplace_modal.installing": "Se instalează ...",
|
||||
"marketplace_modal.list.configure": "Configurați",
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
"about.enterpriseEditione1": "Корпоративная редакция",
|
||||
"about.hash": "Хэш сборки:",
|
||||
"about.hashee": "Хэш сборки EE:",
|
||||
"about.hashwebapp": "Хэш Веб-Приложения:",
|
||||
"about.licensed": "Лицензия зарегистрирована на:",
|
||||
"about.notice": "Mattermost стал возможен благодаря ПО с открытым исходным кодом, используемом в нашем <linkServer>сервере</linkServer>, <linkDesktop>настольном клиенте</linkDesktop> и <linkMobile>мобильном</linkMobile> приложениях.",
|
||||
"about.privacy": "Политика конфиденциальности",
|
||||
@@ -2127,7 +2126,7 @@
|
||||
"admin.service.corsExposedHeadersTitle": "CORS открытые заголовки:",
|
||||
"admin.service.corsHeadersEx": "X-Мой-Заголовок",
|
||||
"admin.service.corsTitle": "Разрешить кроссдоменные запросы от:",
|
||||
"admin.service.developerDesc": "При значении \"да\" на красной панели сверху будут показываться ошибки JavaScript. Не рекомендуется использовать на \"боевом\" сервере. ",
|
||||
"admin.service.developerDesc": "При значении \"да\" на красной панели сверху будут показываться ошибки JavaScript. Не рекомендуется использовать на \"боевом\" сервере.",
|
||||
"admin.service.developerTitle": "Включить режим разработчика: ",
|
||||
"admin.service.disableBotOwnerDeactivatedTitle": "Отключить учетные записи ботов когда их владелец деактивирован:",
|
||||
"admin.service.disableBotWhenOwnerIsDeactivated": "Когда пользователь деактивирован, отключаются все учетные записи ботов, управляемых этим пользователем.Чтобы обратно включить учетные записи ботов, перейдите в [Интеграции > Учетные записи ботов]({siteURL}/_redirect/integrations/bots).",
|
||||
@@ -2648,7 +2647,6 @@
|
||||
"app.channel.post_update_channel_purpose_message.removed": "{username} удалил заголовок канала (было: {old})",
|
||||
"app.channel.post_update_channel_purpose_message.updated_from": "{username} сменил заголовок канала с {old} на {new}",
|
||||
"app.channel.post_update_channel_purpose_message.updated_to": "{username} установил заголовок канала: {new}",
|
||||
"app.plugin.marketplace_plugins.app_error": "Ошибка подключения к серверу магазина плагинов. Проверьте свои настройки в [Системной консоли]({siteURL}/admin_console/plugins/plugin_management).",
|
||||
"apps.error": "Ошибка: {error}",
|
||||
"apps.error.command.field_missing": "Обязательные поля отсутствуют: `{fieldName}`.",
|
||||
"apps.error.command.same_channel": "Канал повторяется для поля `{fieldName}`: `{option}`.",
|
||||
@@ -4081,7 +4079,6 @@
|
||||
"mark_all_threads_as_read_modal.title": "Пометить все свои обсуждения как прочитанные?",
|
||||
"marketplace_command.disabled": "Магазин плагинов отключен. Для получения подробной информации обратитесь к системному администратору.",
|
||||
"marketplace_command.no_permission": "У вас нет соответствующих разрешений для доступа к магазину плагинов.",
|
||||
"marketplace_list.count_total_page": "{startCount, number} - {endCount, number} {total, plural, one {плагин} few {плагина} other {плагинов}} из {total, number}",
|
||||
"marketplace_modal.install_plugins": "Установить плагины",
|
||||
"marketplace_modal.installing": "Устанавливается...",
|
||||
"marketplace_modal.list.configure": "Конфигурация",
|
||||
@@ -4158,7 +4155,7 @@
|
||||
"more.details": "Подробнее",
|
||||
"more_channels.create": "Создать канал",
|
||||
"more_channels.next": "Далее",
|
||||
"more_channels.noMore": "Доступных каналов не найдено",
|
||||
"more_channels.noMore": "Нет результатов поиска для \"{text}\"",
|
||||
"more_channels.prev": "Предыдущая",
|
||||
"more_channels.show_archived_channels": "Показать: Архивированные каналы",
|
||||
"more_channels.show_public_channels": "Показать: Публичные каналы",
|
||||
@@ -4966,11 +4963,7 @@
|
||||
"start_trial.modal.gettingTrial": "Получение пробной версии...",
|
||||
"start_trial.modal.loaded": "Загружено!",
|
||||
"start_trial.modal.loading": "Загрузка...",
|
||||
"start_trial.modal_body": "Получите доступ ко всем функциям платформы, включая расширенную безопасность и соответствие требованиям предприятия.",
|
||||
"start_trial.modal_btn.nottnow": "Не сейчас",
|
||||
"start_trial.modal_btn.start": "Начать бесплатную 30-дневную пробную версию",
|
||||
"start_trial.modal_btn.start_free_trial": "Начать бесплатную 30-дневную пробную версию",
|
||||
"start_trial.modal_title": "Начните бесплатную пробную версию Enterprise прямо сейчас",
|
||||
"start_trial.tutorialTip.desc": "Изучите наши самые востребованные премиум-функции. Определяйте доступ пользователей с помощью гостевых учетных записей, автоматизируйте отчеты о соответствии и отправляйте мобильные push-уведомления только с безопасным идентификатором.",
|
||||
"start_trial.tutorialTip.title": "Попробуйте наши премиум-функции бесплатно",
|
||||
"status_dropdown.dnd_sub_menu_header": "Отключить уведомления до:",
|
||||
@@ -5087,7 +5080,7 @@
|
||||
"threadFromArchivedChannelMessage": "Вы просматриваете обсуждение в **архивированном канале**. На этом канале нельзя опубликовать новые сообщения.",
|
||||
"threading.filters.allThreads": "Все ваши треды",
|
||||
"threading.filters.unreads": "Непрочитанное",
|
||||
"threading.following": "Отслеживание",
|
||||
"threading.following": "Отслеживается",
|
||||
"threading.footer.lastReplyAt": "Последний ответ {formatted}",
|
||||
"threading.header.heading": "Тред",
|
||||
"threading.notFollowing": "Отслеживать",
|
||||
@@ -5105,7 +5098,7 @@
|
||||
"threading.threadMenu.markUnread": "Пометить как непрочитанное",
|
||||
"threading.threadMenu.openInChannel": "Открыт в канале",
|
||||
"threading.threadMenu.save": "Сохранить",
|
||||
"threading.threadMenu.unfollow": "Отключить отслеживание треда",
|
||||
"threading.threadMenu.unfollow": "Прекратить отслеживание треда",
|
||||
"threading.threadMenu.unfollowExtra": "Вы не будете уведомлены об ответах",
|
||||
"threading.threadMenu.unfollowMessage": "Отписаться от сообщения",
|
||||
"threading.threadMenu.unsave": "Убрать из сохраненных",
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
"about.enterpriseEditione1": "Enterprise-utgåva",
|
||||
"about.hash": "Bygg-hash:",
|
||||
"about.hashee": "EE bygg-hash:",
|
||||
"about.hashwebapp": "Webapp bygg-hash:",
|
||||
"about.licensed": "Licensierad till:",
|
||||
"about.notice": "Mattermost är möjlig tack vare att både <linkServer>server</linkServer>, <linkDesktop>skrivbordsapp</linkDesktop> och <linkMobile>mobilapp</linkMobile> har öppen källkod.",
|
||||
"about.privacy": "Integritetspolicy",
|
||||
@@ -2127,7 +2126,7 @@
|
||||
"admin.service.corsExposedHeadersTitle": "CORS Exposed Headers:",
|
||||
"admin.service.corsHeadersEx": "X-My-Header",
|
||||
"admin.service.corsTitle": "Tillåt cross-origin requests från:",
|
||||
"admin.service.developerDesc": "När aktiverad kommer JavaScript-error visas i ett lilafärgat fält högst upp i användargränssnittet. Detta är inte rekommenderat i en produktionsmiljö. ",
|
||||
"admin.service.developerDesc": "När aktiverad kommer JavaScript-error visas i ett lilafärgat fält högst upp i användargränssnittet. Detta är inte rekommenderat i en produktionsmiljö.",
|
||||
"admin.service.developerTitle": "Aktivera utvecklarläge: ",
|
||||
"admin.service.disableBotOwnerDeactivatedTitle": "Spärra bot-konton när dess ägare blir avaktiverad:",
|
||||
"admin.service.disableBotWhenOwnerIsDeactivated": "När en användare blir avaktiverad, spärras alla bot-konton som hanteras av användaren. För att aktivera bot-kontot, gå till [Integrations > Bot Accounts]({siteURL}/_redirect/integrations/bots).",
|
||||
@@ -2648,7 +2647,7 @@
|
||||
"app.channel.post_update_channel_purpose_message.removed": "{username} raderade kanalens syfte (var tidigare {old})",
|
||||
"app.channel.post_update_channel_purpose_message.updated_from": "{username} uppdaterat kanal syfte från: {old} till: {new}",
|
||||
"app.channel.post_update_channel_purpose_message.updated_to": "{username} ändrade kanalens rubrik till: {new}",
|
||||
"app.plugin.marketplace_plugins.app_error": "Fel vid anslutning till marketplace server. Kontrollera inställningarna i [System Console]({siteURL}/admin_console/plugins/plugin_management).",
|
||||
"app_bar.marketplace": "App Marketplace",
|
||||
"apps.error": "Fel: {error}",
|
||||
"apps.error.command.field_missing": "Nödvändiga fält saknas: `{fieldName}`.",
|
||||
"apps.error.command.same_channel": "Kanal upprepas i fältet `{fieldName}`: `{option}`.",
|
||||
@@ -4081,7 +4080,6 @@
|
||||
"mark_all_threads_as_read_modal.title": "Markera alla dina trådar som lästa?",
|
||||
"marketplace_command.disabled": "Marknadsplatsen är inaktiverad. Kontakta din systemadministratör för mer information.",
|
||||
"marketplace_command.no_permission": "Du har inte rätt behörighet att komma åt marknadsplatsen.",
|
||||
"marketplace_list.count_total_page": "{startCount, number} - {endCount, number} {total, plural, one {plugin} other {plugins}} av totalt {total, number}",
|
||||
"marketplace_modal.install_plugins": "Installera plugins",
|
||||
"marketplace_modal.installing": "Installerar...",
|
||||
"marketplace_modal.list.configure": "Konfigurera",
|
||||
@@ -4157,8 +4155,11 @@
|
||||
"modal.manual_status.title_ooo": "Din status är satt till \"Inte på kontoret\"",
|
||||
"more.details": "Mer information",
|
||||
"more_channels.create": "Skapa kanal",
|
||||
"more_channels.createClick": "Klicka på 'Skapa ny kanal' för att skapa en ny",
|
||||
"more_channels.join": "Gå med",
|
||||
"more_channels.joining": "Går med...",
|
||||
"more_channels.next": "Nästa",
|
||||
"more_channels.noMore": "Det finns inga fler kanaler att gå med i",
|
||||
"more_channels.noMore": "Inget resultat för \"{text}\"",
|
||||
"more_channels.prev": "Föregående",
|
||||
"more_channels.show_archived_channels": "Visa: Arkiverade kanaler",
|
||||
"more_channels.show_public_channels": "Publika kanaler",
|
||||
@@ -4224,7 +4225,7 @@
|
||||
"navbar_dropdown.logout": "Logga ut",
|
||||
"navbar_dropdown.manageGroups": "Hantera grupper",
|
||||
"navbar_dropdown.manageMembers": "Hantera medlemmar",
|
||||
"navbar_dropdown.marketplace": "Marketplace",
|
||||
"navbar_dropdown.marketplace": "App Marketplace",
|
||||
"navbar_dropdown.menuAriaLabel": "Huvudmeny",
|
||||
"navbar_dropdown.nativeApps": "Ladda ner app",
|
||||
"navbar_dropdown.profileSettings": "Profil",
|
||||
@@ -4369,6 +4370,7 @@
|
||||
"payment_form.no_billing_address": "Ingen fakturaadress tillagd",
|
||||
"payment_form.no_credit_card": "Inget betalkort tillagt",
|
||||
"payment_form.saved_payment_method": "Spara betalmetod",
|
||||
"payment_form.shipping_address": "Leveransadress",
|
||||
"payment_form.zipcode": "ZIP/Postnummer",
|
||||
"pending_post_actions.cancel": "Avbryt",
|
||||
"pending_post_actions.retry": "Försök igen",
|
||||
@@ -4870,6 +4872,8 @@
|
||||
"sidebar.types.favorites": "FAVORITER",
|
||||
"sidebar.types.unreads": "OLÄSTA",
|
||||
"sidebar.unreads": "Fler olästa meddelanden",
|
||||
"sidebar_left.addChannelsCta": "Lägg till kanaler",
|
||||
"sidebar_left.add_channel_cta_dropdown.dropdownAriaLabel": "Lägg till en rullgardinsmeny för kanaler",
|
||||
"sidebar_left.add_channel_dropdown.browseChannels": "Bläddra bland kanalerna",
|
||||
"sidebar_left.add_channel_dropdown.browseOrCreateChannels": "Bläddra eller skapa kanaler",
|
||||
"sidebar_left.add_channel_dropdown.createCategory": "Skapa ny kategori",
|
||||
@@ -4918,6 +4922,7 @@
|
||||
"sidebar_left.sidebar_channel_menu.unfavoriteChannel": "Ta bort favorit",
|
||||
"sidebar_left.sidebar_channel_menu.unmuteChannel": "Ljud på i kanalen",
|
||||
"sidebar_left.sidebar_channel_menu.unmuteConversation": "Ljud på i konversationen",
|
||||
"sidebar_left.sidebar_channel_navigator.addChannelsCta": "Lägg till kanaler",
|
||||
"sidebar_left.sidebar_channel_navigator.inviteUsers": "Bjud in användare",
|
||||
"sidebar_right_menu.console": "Systemkonsol",
|
||||
"sidebar_right_menu.flagged": "Markerade meddelanden",
|
||||
@@ -4966,11 +4971,7 @@
|
||||
"start_trial.modal.gettingTrial": "Hämtar prova-på-period...",
|
||||
"start_trial.modal.loaded": "Laddad!",
|
||||
"start_trial.modal.loading": "Laddar...",
|
||||
"start_trial.modal_body": "Få tillgång till alla funktioner inklusive avancerad säkerhet och efterlevnadskontroll för företags.",
|
||||
"start_trial.modal_btn.nottnow": "Inte nu",
|
||||
"start_trial.modal_btn.start": "Starta en kostnadsfri 30-dagars prova-på-period",
|
||||
"start_trial.modal_btn.start_free_trial": "Starta en kostnadsfri 30-dagars prova-på-period",
|
||||
"start_trial.modal_title": "Starta din kostnadsfria prova-på-period för Enterprise nu",
|
||||
"start_trial.tutorialTip.desc": "Utforska våra mest efterfrågade premiumfunktioner. Bestäm användarnas åtkomst med gästkonton, automatisera rapporter om efterlevnad och skicka informationsskyddade notifieringar till mobilen.",
|
||||
"start_trial.tutorialTip.title": "Testa våra premiumfunktioner gratis",
|
||||
"status_dropdown.dnd_sub_menu_header": "Inaktivera notifieringar till:",
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
"about.enterpriseEditione1": "Enterprise paketi",
|
||||
"about.hash": "Yapım karması:",
|
||||
"about.hashee": "Enterprise paketi yapım karması:",
|
||||
"about.hashwebapp": "Web uygulaması yapım karması:",
|
||||
"about.licensed": "Lisans sahibi:",
|
||||
"about.notice": "Kullandığımız açık kaynaklı <linkServer>sunucu</linkServer>, <linkDesktop>masaüstü</linkDesktop> ve <linkMobile>mobil</linkMobile> uygulamalari Mattermost tarafından sunulmaktadır.",
|
||||
"about.privacy": "Kişisel verilerin gizliliği ilkesi",
|
||||
@@ -2606,7 +2605,6 @@
|
||||
"app.channel.post_update_channel_purpose_message.removed": "{username} kanal amacını kaldırdı (önceki: {old})",
|
||||
"app.channel.post_update_channel_purpose_message.updated_from": "{username}, {old} eski kanal amacını {new} olarak değiştirdi",
|
||||
"app.channel.post_update_channel_purpose_message.updated_to": "{username} kanal amacını {new} olarak güncelledi",
|
||||
"app.plugin.marketplace_plugins.app_error": "Mağaza sunucusu ile bağlantı kurulurken sorun çıktı. Lütfen [Sistem panosu]({siteURL}/admin_console/plugins/plugin_management) üzerinden ayarlarınızı denetleyin.",
|
||||
"apps.error": "Hata: {error}",
|
||||
"apps.error.command.field_missing": "Zorunlu alan eksik: `{fieldName}`.",
|
||||
"apps.error.command.same_channel": "`{fieldName}`alanında kanal yineleniyor: `{option}`.",
|
||||
@@ -4019,7 +4017,6 @@
|
||||
"mark_all_threads_as_read_modal.title": "Tüm konularınız okunmuş olarak işaretlensin mi?",
|
||||
"marketplace_command.disabled": "Mağaza devre dışı bırakılmış. Lütfen ayrıntılı bilgi almak için sistem yöneticiniz ile görüşün.",
|
||||
"marketplace_command.no_permission": "Mağazaya erişmek için yeterli izinleriniz yok.",
|
||||
"marketplace_list.count_total_page": "{startCount, number} - {endCount, number} {total, plural, one {uygulama eki} other {uygulama eki}} / {total, number}",
|
||||
"marketplace_modal.install_plugins": "Uygulama ekleri kur",
|
||||
"marketplace_modal.installing": "Kuruluyor...",
|
||||
"marketplace_modal.list.configure": "Yapılandır",
|
||||
@@ -4867,11 +4864,7 @@
|
||||
"start_trial.modal.gettingTrial": "Deneme sürümü alınıyor...",
|
||||
"start_trial.modal.loaded": "Yüklendi!",
|
||||
"start_trial.modal.loading": "Yükleniyor...",
|
||||
"start_trial.modal_body": "Gelişmiş güvenlik ve kurumsal uyumluluk ile tüm platform özelliklerine erişin.",
|
||||
"start_trial.modal_btn.nottnow": "Şimdi değil",
|
||||
"start_trial.modal_btn.start": "30 günlük ücretsiz denemeyi başlat",
|
||||
"start_trial.modal_btn.start_free_trial": "30 günlük ücretsiz denemeyi başlat",
|
||||
"start_trial.modal_title": "Ücretsiz Enterprise tarifesi deneme sürenizi başlatın",
|
||||
"start_trial.tutorialTip.desc": "En çok istenilen ücretli özelliklerimizi keşfedin. Konuk hesapları ile kullanıcı erişimini sınırlayın, uyumluluk raporlarını otomatikleştirin ve yalnızca kimliğe özel güvenli mobil anında iletileri gönderin.",
|
||||
"start_trial.tutorialTip.title": "Ücretli özelliklerimizi ücretsiz olarak deneyin",
|
||||
"status_dropdown.dnd_sub_menu_header": "Bildirimler şu zamana kadar kapatılsın:",
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
"about.enterpriseEditione1": "Enterprise Edition",
|
||||
"about.hash": "Хеш збірки:",
|
||||
"about.hashee": "Хеш збірки EE:",
|
||||
"about.hashwebapp": "Хеш збірки Webapp:",
|
||||
"about.licensed": "Ліцензовано на:",
|
||||
"about.notice": "Mattermost це стало можливим за допомогою програмного забезпечення з відкритим кодом, яке використовується на нашому сервері , <linkServer>desktop</linkServer> та <linkDesktop>mobile</linkDesktop> додатків.",
|
||||
"about.privacy": "Політика конфіденційності",
|
||||
@@ -2193,7 +2192,6 @@
|
||||
"login.verified": "Електронну пошту підтверджено",
|
||||
"login_mfa.submit": "Відправити",
|
||||
"login_mfa.token": "Токен MFA",
|
||||
"marketplace_list.count_total_page": "{startCount, number} - {endCount, number} {count, plural, one {user} other {users}} of {total, number} total",
|
||||
"marketplace_modal.install_plugins": "Встановлені плагіни:",
|
||||
"marketplace_modal.list.try_again": "Спробуй ще раз ",
|
||||
"marketplace_modal.list.update": "Оновити",
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
"about.enterpriseEditione1": "企业版",
|
||||
"about.hash": "编译哈希:",
|
||||
"about.hashee": "企业版编译哈希:",
|
||||
"about.hashwebapp": "网页应用编译哈系:",
|
||||
"about.licensed": "授权给:",
|
||||
"about.notice": "开源软件帮助 Mattermost 实现了我们的<linkServer>服务端</linkServer>、<linkDesktop>桌面</linkDesktop>以及<linkMobile>移动</linkMobile>应用。",
|
||||
"about.privacy": "隐私政策",
|
||||
@@ -319,7 +318,16 @@
|
||||
"admin.billing.subscription.creditCardExpired": "您的信用卡已过期。请更新您的付款信息,以免造成任何中断。",
|
||||
"admin.billing.subscription.creditCardHasExpired": "您的信用卡已过期",
|
||||
"admin.billing.subscription.creditCardHasExpired.description": "请更新您的 <link>付款信息</link> 以避免任何中断服务。",
|
||||
"admin.billing.subscription.deleteWorkspaceModal.cancelButton": "保持订阅",
|
||||
"admin.billing.subscription.deleteWorkspaceModal.deleteButton": "删除工作区",
|
||||
"admin.billing.subscription.deleteWorkspaceModal.downgradeButton": "降级为免费",
|
||||
"admin.billing.subscription.deleteWorkspaceModal.title": "您确定要删除吗?",
|
||||
"admin.billing.subscription.deleteWorkspaceModal.usage": "作为你付费订阅Mattermost {sku} 的一部分,你目前创建了 ",
|
||||
"admin.billing.subscription.deleteWorkspaceModal.usageDetails": "{messageCount} 条消息和 {fileSize} 的文件",
|
||||
"admin.billing.subscription.deleteWorkspaceModal.warning": "删除您的工作区是决定性的操作。在删除时,您将失去所有相关数据并且没有能力进行恢复。如果您降级到免费版,您将仍然能看到此信息。",
|
||||
"admin.billing.subscription.deleteWorkspaceSection.delete": "删除工作区",
|
||||
"admin.billing.subscription.deleteWorkspaceSection.description": "删除 {workspaceLink} 是最终决定并且以后不能恢复。",
|
||||
"admin.billing.subscription.deleteWorkspaceSection.title": "删除你的工作区",
|
||||
"admin.billing.subscription.downgradedSuccess": "您现在订阅了 {productName}",
|
||||
"admin.billing.subscription.downgrading": "降级您的工作区",
|
||||
"admin.billing.subscription.featuresAvailable": "{productName} 功能现在已可以使用。",
|
||||
@@ -370,6 +378,7 @@
|
||||
"admin.billing.subscription.planDetails.userCount": "{userCount} 位用户",
|
||||
"admin.billing.subscription.privateCloudCard.cloudEnterprise.description": "在 Mattermost,我们与您和您的团队合作,以满足您对整个产品的需求。如果您正在寻找年度折扣,请联系我们的销售团队。",
|
||||
"admin.billing.subscription.privateCloudCard.cloudEnterprise.title": "在寻找年度折扣吗? ",
|
||||
"admin.billing.subscription.privateCloudCard.cloudFree.description": "使用访客账户,Office365套件集起,GitLab SSO和高级权限优化你的流程。",
|
||||
"admin.billing.subscription.privateCloudCard.cloudFree.title": "升级至 Cloud Professional 版",
|
||||
"admin.billing.subscription.privateCloudCard.cloudProfessional.description": "拥有高级支持的高级安全性和合规性功能。详细信息请参阅 {pricingLink}。",
|
||||
"admin.billing.subscription.privateCloudCard.cloudProfessional.title": "升级到企业级云",
|
||||
@@ -387,6 +396,7 @@
|
||||
"admin.billing.subscription.providePaymentDetails": "提供您的付款明细",
|
||||
"admin.billing.subscription.returnToTeam": "返回到{team}",
|
||||
"admin.billing.subscription.stateprovince": "州/省",
|
||||
"admin.billing.subscription.switchedToAnnual.title": "您现在的切换到了 {selectedProductName}",
|
||||
"admin.billing.subscription.title": "订阅",
|
||||
"admin.billing.subscription.updatePaymentInfo": "更新付款信息",
|
||||
"admin.billing.subscription.upgradedSuccess": "您现在已升级到{productName}",
|
||||
@@ -394,6 +404,8 @@
|
||||
"admin.billing.subscription.userCount.tooltipTitle": "当前用户数",
|
||||
"admin.billing.subscription.verifyPaymentInformation": "验证您的付款信息中",
|
||||
"admin.billing.subscription.viewBilling": "查看账单",
|
||||
"admin.billing.subscriptions.billing_summary.explore_enterprise": "浏览企业版功能",
|
||||
"admin.billing.subscriptions.billing_summary.explore_enterprise.cta": "查看所有功能",
|
||||
"admin.billing.subscriptions.billing_summary.lastInvoice.failed": "失败",
|
||||
"admin.billing.subscriptions.billing_summary.lastInvoice.monthlyFlatFee": "每月固定费用",
|
||||
"admin.billing.subscriptions.billing_summary.lastInvoice.paid": "已支付",
|
||||
@@ -405,11 +417,26 @@
|
||||
"admin.billing.subscriptions.billing_summary.lastInvoice.total": "总计",
|
||||
"admin.billing.subscriptions.billing_summary.lastInvoice.userCount": " x {users} 用户",
|
||||
"admin.billing.subscriptions.billing_summary.lastInvoice.userCountPartial": "{users} 用户",
|
||||
"admin.billing.subscriptions.billing_summary.lastInvoice.viewInvoice": "查看发票",
|
||||
"admin.billing.subscriptions.billing_summary.lastInvoice.whatArePartialCharges": "什么是部分费用?",
|
||||
"admin.billing.subscriptions.billing_summary.lastInvoice.whatArePartialCharges.message": "在一个月的整个时间内未启用的用户将按比例按月计费。",
|
||||
"admin.billing.subscriptions.billing_summary.noBillingHistory.description": "将来,这是您最近的帐单摘要将显示的位置。",
|
||||
"admin.billing.subscriptions.billing_summary.noBillingHistory.link": "查看计费方式",
|
||||
"admin.billing.subscriptions.billing_summary.noBillingHistory.title": "尚无帐单记录",
|
||||
"admin.billing.subscriptions.billing_summary.try_enterprise": "免费试用企业版功能",
|
||||
"admin.billing.subscriptions.billing_summary.try_enterprise.cta": "免费试用{trialLength} 天",
|
||||
"admin.billing.subscriptions.billing_summary.upcomingInvoice.has_more_line_items": "有{count}更多条目",
|
||||
"admin.billing.subscriptions.billing_summary.upgrade_professional": "升级至专业套餐",
|
||||
"admin.billing.subscriptions.billing_summary.upgrade_professional.cta": "升级",
|
||||
"admin.billing.trueUpReview.button_download": "下载数据",
|
||||
"admin.billing.trueUpReview.button_share": "分享到Mattermost",
|
||||
"admin.billing.trueUpReview.docsLinkCTA": "了解更多关于true-up.",
|
||||
"admin.billing.trueUpReview.due_date": "到期 ",
|
||||
"admin.billing.trueUpReview.share_data_for_review": "与Mattermost分享您的系统统计数据以进行季度的True-Up审核。{link}",
|
||||
"admin.billing.trueUpReview.submit.thanks_for_sharing": "谢谢您分享与True-Up审核相关的数据。",
|
||||
"admin.billing.trueUpReview.submit_error": "发送您的TrueUp审核时发生错误。请重试。",
|
||||
"admin.billing.trueUpReview.submit_success": "成功!",
|
||||
"admin.billing.trueUpReview.title": "True Up审核",
|
||||
"admin.bleve.bulkIndexingTitle": "批量索引:",
|
||||
"admin.bleve.createJob.help": "数据库中所有的用户、频道以及消息将从旧到新顺序索引。Bleve 可以在索引过程中使用但搜索结果可能不完整。",
|
||||
"admin.bleve.createJob.title": "立刻索引",
|
||||
@@ -531,6 +558,7 @@
|
||||
"admin.cluster.status_table.url": "Gossip 地址",
|
||||
"admin.cluster.status_table.version": "版本",
|
||||
"admin.cluster.unknown": "未知",
|
||||
"admin.cluster.version_mismatch_warning": "警告:您的高可用集群检测出多个版本的Mattermost。如果您不是正在进行升级,请确认集群所有的节点使用相同的Mattermost版本,以防平台功能异常。",
|
||||
"admin.compliance.complianceMonitoring": "合规监视",
|
||||
"admin.compliance.directoryDescription": "用户保存守规报告。如果为空,将被设置为 ./data/。",
|
||||
"admin.compliance.directoryExample": "例如 \"./data/\"",
|
||||
@@ -604,6 +632,8 @@
|
||||
"admin.connectionSecurityTlsDescription": "加密Mattermost和您的服务器之间的通信。",
|
||||
"admin.custom_terms_of_service_feature_discovery.copy": "创建您自己的服务条款,新用户在访问桌面、网页或移动设备上的 Mattermost 之前必须接受该条款。",
|
||||
"admin.custom_terms_of_service_feature_discovery.title": "使用 Mattermost Enterprise 创建自定义服务条款",
|
||||
"admin.customization.allowSyncedDrafts": "开启服务端草稿消息同步:",
|
||||
"admin.customization.allowSyncedDraftsDesc": "当开启,用户的消息草稿将同步至服务端,所有的设备都可以看到。用户可以在帐户设置里关闭这个功能。",
|
||||
"admin.customization.androidAppDownloadLinkDesc": "添加安卓应用下载链接。用移动设备访问的用户将看到应用下载提示页面。此栏留空将不显示。",
|
||||
"admin.customization.androidAppDownloadLinkTitle": "安卓应用下载链接:",
|
||||
"admin.customization.announcement.allowBannerDismissalDesc": "当设为是时,用户可以撤掉横幅直到下次更新。当设为否时,横幅将永久显示直到被系统管理员关闭。",
|
||||
@@ -633,6 +663,8 @@
|
||||
"admin.customization.enableLinkPreviewsTitle": "启用网站链接预览:",
|
||||
"admin.customization.enablePermalinkPreviewsDesc": "启用后,指向 Mattermost 消息的链接将为有权访问原始消息的任何用户生成预览。请查看我们的<link>文档</link>了解详细信息。",
|
||||
"admin.customization.enablePermalinkPreviewsTitle": "开启消息链接预览:",
|
||||
"admin.customization.enablePublicSharedBoardsDesc": "此项允许面板编辑使用链接分享面板给所有人访问。",
|
||||
"admin.customization.enablePublicSharedBoardsTitle": "启用公共访问面板:",
|
||||
"admin.customization.enableSVGsDesc": "开启 SVG 附件预览并让它们显示在消息中。",
|
||||
"admin.customization.enableSVGsTitle": "开启 SVG:",
|
||||
"admin.customization.gfycatApiKey": "Gfycat API Key:",
|
||||
@@ -708,6 +740,12 @@
|
||||
"admin.database.title": "数据库",
|
||||
"admin.developer.title": "开发人员设置",
|
||||
"admin.elasticsearch.bulkIndexingTitle": "批量索引:",
|
||||
"admin.elasticsearch.caExample": "例:\"./elasticsearch/ca.pem\"",
|
||||
"admin.elasticsearch.caTitle": "CA path:",
|
||||
"admin.elasticsearch.clientCertExample": "例:\"./elasticsearch/client-cert.pem\"",
|
||||
"admin.elasticsearch.clientCertTitle": "客户端证书路径:",
|
||||
"admin.elasticsearch.clientKeyExample": "例:\"./elasticsearch/client-cert.pem\"",
|
||||
"admin.elasticsearch.clientKeyTitle": "客户端证书钥匙路径:",
|
||||
"admin.elasticsearch.connectionUrlDescription": "Elasticsearch 服务器地址。{documentationLink}",
|
||||
"admin.elasticsearch.connectionUrlExample": "例如:\"https://elasticsearch.example.org:9200\"",
|
||||
"admin.elasticsearch.connectionUrlExample.documentationLinkText": "请见服务器架设说明文档。",
|
||||
@@ -798,6 +836,9 @@
|
||||
"admin.environment.notifications.replyToAddress.label": "通知 Reply-To 地址:",
|
||||
"admin.environment.notifications.replyToAddress.placeholder": "例如:\"mattermost@yourcompany.com\", \"admin@yourcompany.com\"",
|
||||
"admin.environment.notifications.supportAddress.placeholder": "例:“support@yourcompany.com”,“admin@yourcompany.com”",
|
||||
"admin.environment.notifications.supportEmail.help": "客户支持邮件里显示的电子邮件地址。",
|
||||
"admin.environment.notifications.supportEmail.label": "客户支持邮件地址:",
|
||||
"admin.environment.notifications.supportEmail.required": "需要“客户支持电子邮件地址”",
|
||||
"admin.environment.pushNotificationServer": "推送通知服务器",
|
||||
"admin.environment.smtp": "SMTP",
|
||||
"admin.environment.smtp.connectionSecurity.option.none": "无",
|
||||
@@ -812,6 +853,7 @@
|
||||
"admin.environment.smtp.smtpAuth.description": "当设为是时,开启 SMTP 验证。",
|
||||
"admin.environment.smtp.smtpAuth.title": "开启 SMTP 验证:",
|
||||
"admin.environment.smtp.smtpFail": "连接失败:{error}",
|
||||
"admin.environment.smtp.smtpFailure": "SMTP没有在在系统控制台里配置。可以在 <a>这里</a>配置。",
|
||||
"admin.environment.smtp.smtpPassword.description": "从邮件服务器管理员获得此凭据。",
|
||||
"admin.environment.smtp.smtpPassword.placeholder": "例如:\"yourpassword\"、\"jcuS8PuvcpGhpgHhlcpT1Mx42pnqMxQY\"",
|
||||
"admin.environment.smtp.smtpPassword.title": "SMTP 服务器密码:",
|
||||
@@ -2427,7 +2469,6 @@
|
||||
"app.channel.post_update_channel_purpose_message.removed": "{username} 移除了频道作用 (曾是:{old})",
|
||||
"app.channel.post_update_channel_purpose_message.updated_from": "{username} 更新了频道作用从:{old} 到:{new}",
|
||||
"app.channel.post_update_channel_purpose_message.updated_to": "{username} 更新了频道作用到:{new}",
|
||||
"app.plugin.marketplace_plugins.app_error": "连接到集市服务器出错。请检查[系统控制台]({siteURL}/admin_console/plugins/plugin_management)的设定。",
|
||||
"apps.error": "错误:{error}",
|
||||
"apps.error.command.field_missing": "缺少必填字段:`{fieldName}`。",
|
||||
"apps.error.command.same_channel": "频道重复,字段 `{fieldName}`:`{option}`。",
|
||||
@@ -3525,7 +3566,6 @@
|
||||
"manage_channel_groups_modal.search_placeholder": "搜索组",
|
||||
"manage_team_groups_modal.search_placeholder": "搜索组",
|
||||
"mark_all_threads_as_read_modal.cancel": "取消",
|
||||
"marketplace_list.count_total_page": "{startCount, number} - {endCount, number} {count} 个,共 {total, number} 个",
|
||||
"marketplace_modal.install_plugins": "安装插件",
|
||||
"marketplace_modal.installing": "安装中...",
|
||||
"marketplace_modal.list.configure": "配置",
|
||||
@@ -4043,9 +4083,6 @@
|
||||
"start_trial.modal.failed": "失败",
|
||||
"start_trial.modal.loaded": "已加载!",
|
||||
"start_trial.modal.loading": "加载中...",
|
||||
"start_trial.modal_btn.nottnow": "现在不要",
|
||||
"start_trial.modal_btn.start": "开始 30 天试用",
|
||||
"start_trial.modal_title": "立即开始您的免费企业版试用",
|
||||
"status_dropdown.dnd_sub_menu_header": "禁用通知,直到:",
|
||||
"status_dropdown.dnd_sub_menu_item.custom": "自定义",
|
||||
"status_dropdown.dnd_sub_menu_item.one_hour": "1 小时",
|
||||
@@ -4127,7 +4164,7 @@
|
||||
"team_settings_modal.title": "团队设置",
|
||||
"team_sidebar.join": "您可以加入的其他团队",
|
||||
"terms_of_service.agreeButton": "我同意",
|
||||
"terms_of_service.api_error": "无法完成请求。如果此问题持续,请联系您的系统管理员。",
|
||||
"terms_of_service.api_error": "无法完成请求。如果此问题仍然存在,请联系您的系统管理员。",
|
||||
"terms_of_service.disagreeButton": "我不同意",
|
||||
"test": "文本按钮",
|
||||
"textbox.bold": "**加粗**",
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
"about.enterpriseEditione1": "企業版",
|
||||
"about.hash": "編譯 Hash:",
|
||||
"about.hashee": "企業版編譯 Hash:",
|
||||
"about.hashwebapp": "網頁程式建置雜湊:",
|
||||
"about.licensed": "授權給:",
|
||||
"about.notice": "藉由在<linkServer>伺服器</linkServer>、<linkDesktop>桌面</linkDesktop>與<linkMobile>行動裝置</linkMobile>上使用的開源軟體,才得以實現 Mattermost。",
|
||||
"about.privacy": "隱私政策",
|
||||
@@ -1929,7 +1928,6 @@
|
||||
"app.channel.post_update_channel_purpose_message.removed": "{username} 已移除頻道用途(原為:{old})",
|
||||
"app.channel.post_update_channel_purpose_message.updated_from": "{username} 已更新頻道用途:從 {old} 改為 {new}",
|
||||
"app.channel.post_update_channel_purpose_message.updated_to": "{username} 已更新頻道用途為:{new}",
|
||||
"app.plugin.marketplace_plugins.app_error": "連線至市集伺服器失敗。請檢查[系統控制台]({siteURL}/admin_console/plugins/plugin_management)的設定。",
|
||||
"apps.error": "錯誤:{error}",
|
||||
"apps.error.command.field_missing": "遺漏必填欄位:`{fieldName}`。",
|
||||
"apps.error.command.same_channel": "頻道重複的欄位 `{fieldName}`:`{option}`。",
|
||||
@@ -2848,7 +2846,6 @@
|
||||
"login_mfa.token": "多重要素驗證 Token",
|
||||
"manage_channel_groups_modal.search_placeholder": "搜尋群組",
|
||||
"manage_team_groups_modal.search_placeholder": "搜尋群組",
|
||||
"marketplace_list.count_total_page": "{total, number}位中{startCount, number} - {endCount, number}位使用者",
|
||||
"marketplace_modal.install_plugins": "安裝模組",
|
||||
"marketplace_modal.installing": "安裝中...",
|
||||
"marketplace_modal.list.configure": "設定",
|
||||
|
||||
|
До Ширина: | Высота: | Размер: 22 KiB После Ширина: | Высота: | Размер: 259 KiB |
|
До Ширина: | Высота: | Размер: 80 KiB После Ширина: | Высота: | Размер: 419 KiB |
Двоичные данные
webapp/channels/src/images/worktemplates/boards/roadmap.png
|
До Ширина: | Высота: | Размер: 68 KiB После Ширина: | Высота: | Размер: 296 KiB |
|
До Ширина: | Высота: | Размер: 105 KiB После Ширина: | Высота: | Размер: 306 KiB |
|
До Ширина: | Высота: | Размер: 68 KiB После Ширина: | Высота: | Размер: 236 KiB |
|
До Ширина: | Высота: | Размер: 82 KiB После Ширина: | Высота: | Размер: 568 KiB |
Двоичные данные
webapp/channels/src/images/worktemplates/companywide/create_project/create_project.png
Обычный файл
|
После Ширина: | Высота: | Размер: 119 KiB |