\n {/*
*/}\n
\n \n Cancel\n \n \n
\n
\n \n )\n }\n\n isNameValid = () => {\n const { swimlane, swimlanes } = this.props\n const title = this.state.swimlaneTitle.trim().toLowerCase()\n\n if (title === 'all' || /\\s/g.test(title)) {\n return false\n }\n\n let filteredSwimlanes = swimlanes\n\n if (swimlanes) {\n if (swimlane) {\n filteredSwimlanes = filteredSwimlanes.filter(\n sl => sl.get('srn') !== swimlane.get('srn')\n )\n }\n\n if (\n filteredSwimlanes.find(\n swimlane => swimlane.get('title', '').toLowerCase() === title\n )\n ) {\n return false\n }\n }\n\n return true\n }\n\n render() {\n const nameIsValid = this.isNameValid()\n\n return (\n
\n
\n \n Edit Swimlane Details\n \n \n \n \n {this.renderButtons(nameIsValid)} \n
\n
\n )\n }\n}\n\nEditDetails.propTypes = {\n theme: themeShape,\n swimlane: ImmutablePropTypes.map,\n swimlanes: ImmutablePropTypes.map,\n save: PropTypes.func,\n close: PropTypes.func,\n toggleModal: PropTypes.func,\n updating: PropTypes.bool,\n}\n\nexport default themeable(EditDetails)\n","import React, { Component } from 'react'\nimport PropTypes from 'prop-types'\nimport ImmutablePropTypes from 'react-immutable-proptypes'\n\nimport { connect } from 'react-redux'\nimport { createStructuredSelector } from 'reselect'\nimport { compose, bindActionCreators } from 'redux'\nimport { push } from 'connected-react-router'\nimport qs from 'query-string'\nimport _ from 'lodash'\nimport { Modal, ModalBody, ModalHeader, ModalFooter, Alert } from 'reactstrap'\n\nimport CloudBadge from 'components/CloudBadge'\nimport permissionChecker from 'containers/PermissionChecker'\nimport { ToolbarItem } from 'components/BulkActionToolbar'\nimport {\n selectIsUpdatingBotAssignment,\n selectBots,\n} from 'containers/SonraiData/selectors'\nimport { updateBotAssignments, getBots } from 'containers/SonraiData/actions'\nimport SectionHeader from 'components/SectionHeader'\nimport TextLink from 'components/TextLink'\nimport WithPermission from 'containers/PermissionChecker/WithPermission'\nimport { List, fromJS } from 'immutable'\nimport DataTable from 'components/DataTable'\nimport Button from 'components/Button'\nimport Icon from 'components/Icon'\nimport SelectBar from 'components/SelectBar'\nimport SquareLoadingAnimation from 'components/SquareLoadingAnimation'\nimport themeable, { themeShape } from 'containers/ThemeManager/Themeable'\nimport { getAcceptableCloudAccountTypesForBots } from 'utils/sonraiUtils'\n\nimport { setSwimlaneBotError as setError, updateSwimlane } from './actions'\nimport { selectSwimlaneBotError, selectUpdatingSwimlanes } from './selectors'\nclass SwimlaneBots extends Component {\n styles = {\n contentWrapper: {\n height: '100%',\n display: 'grid',\n gridTemplateRows: 'auto auto auto 1fr',\n gridTemplateAreas: '\"header\" \"instructions\" \"error\" \"table\"',\n },\n body: {\n gridArea: 'table',\n },\n alert: { marginTop: '0.25em', marginBottom: '-0.5em', gridArea: 'error' },\n preventionYes: {\n color: this.props.theme.success,\n borderColor: this.props.theme.success,\n marginRight: '1em',\n },\n preventionNah: {\n color: this.props.theme.fail,\n borderColor: this.props.theme.fail,\n marginRight: '1em',\n },\n swimlaneTitleWrapper: {\n gridArea: 'header',\n display: 'grid',\n gridTemplateColumns: '1fr auto auto',\n },\n }\n\n constructor(props) {\n super(props)\n if (props.allBots.get('data').isEmpty()) {\n props.getBots({ filters: {} })\n }\n this.state = {\n selectedBots: List(),\n addBotsSelected: [],\n addBotModalOpen: false,\n errorMsg: null,\n }\n }\n\n componentDidUpdate(prevProps, prevState) {\n if (prevProps.error && !prevState.errorMsg) {\n this.setError(prevProps.error)\n }\n }\n\n toggleBotModal = () => {\n this.setState(prevState => ({\n addBotModalOpen: !prevState.addBotModalOpen,\n }))\n }\n\n closeAddBotModal = () => {\n this.setState({ addBotModalOpen: false, addBotsSelected: [] })\n }\n\n handleAddBotsSelectChange = bots => {\n if (!bots) {\n this.setState({ addBotsSelected: [] })\n } else {\n this.setState({ addBotsSelected: bots })\n }\n }\n\n addBots = () => {\n this.props.updateBotAssignments({\n swimlaneSrn: this.props.swimlane.get('srn'),\n add: this.state.addBotsSelected.map(bot => {\n return {\n botSrn: bot.value,\n contentSrn: this.props.swimlane.get('srn'),\n }\n }),\n })\n this.setState({ addBotModalOpen: false })\n }\n\n unassignBots = () => {\n this.props.updateBotAssignments({\n swimlaneSrn: this.props.swimlane.get('srn'),\n remove: this.state.selectedBots.toJS(),\n })\n this.setState({ selectedBots: List() })\n }\n\n getBotBadge = botSrn => {\n const bot = this.props.allBots\n .get('data', List())\n .find(bot => bot.get('srn') === botSrn)\n\n return (\n
\n \n {bot.get('title')}\n \n )\n }\n\n getModalBody = () => {\n const swimlaneAccounts = this.props.swimlane.get('accounts') || List()\n const currentBotSrns = (\n this.props.swimlane.get('bots') || List()\n ).map(bot => bot.get('srn'))\n const accountTypes = getAcceptableCloudAccountTypesForBots(\n swimlaneAccounts,\n this.props.accounts\n )\n const allBots = this.props.allBots\n .get('data')\n .filter(item => item)\n .filter(bot => {\n if (!_.isEmpty(accountTypes)) {\n if (accountTypes.includes(bot.get('cloud').toLowerCase())) {\n return true\n } else {\n return false\n }\n }\n return true\n })\n .map(bot => ({\n value: bot.get('srn'),\n label: bot.get('title'),\n }))\n .filter(guy => !currentBotSrns.includes(guy.value))\n\n if (this.props.allBots.get('isLoading')) {\n return
\n }\n\n return (\n
\n this.getBotBadge(option.value)}\n />\n
\n )\n }\n\n formatData = () => {\n return this.props.swimlane\n .get('bots')\n .toJS()\n .map(bot => {\n return {\n cloud: bot.cloud,\n name: bot.title,\n description: bot.description,\n srn: bot.srn,\n botAssignmentSrn: bot.botAssignmentSrn,\n }\n })\n }\n\n handleSelectionChanged = event => {\n const rows = event.api.getSelectedRows() || []\n const selectedBots = []\n rows.forEach(row => {\n selectedBots.push(row.botAssignmentSrn)\n })\n this.setState({\n selectedBots: fromJS(selectedBots),\n })\n }\n\n clearError = () => {\n this.setState({ errorMsg: null })\n }\n\n setError = msg => {\n this.setState({\n errorMsg: msg,\n selectedBots: List(),\n addBotsSelected: [],\n addBotModalOpen: false,\n })\n this.props.setError(null)\n setTimeout(() => {\n this.setState({ errorMsg: null })\n }, 5000)\n }\n\n handleBotNav = srn => {\n if (srn) {\n this.props.push({\n pathname: '/App/BotManagement/BotDetails',\n search: qs.stringify({\n botId: srn,\n }),\n })\n }\n }\n\n toggleBotsEnabled = () => {\n this.props.updateSwimlane({\n srn: this.props.swimlane.get('srn'),\n swimlane: {\n ...this.props.swimlane.toJS(),\n preventionEnabled: this.props.swimlane.get('preventionEnabled')\n ? false\n : true,\n },\n })\n }\n\n render() {\n const canAssign = this.props.userHasPermission({\n permissionName: 'assign.bots',\n resourceId: this.props.swimlane.get('resourceId'),\n })\n\n return (\n
\n
\n
Prevention Bots\n
\n \n Preventions are{' '}\n \n {this.props.swimlane.get('preventionEnabled')\n ? 'Enabled'\n : 'Disabled'}\n \n \n \n \n \n
\n\n
\n \n \n
\n
\n Prevention bots apply cloud-native policies to all accounts included\n in this swimlane. These policies can block functionality in your\n environment, so carefully review which preventions you enable.\n
\n {this.state.errorMsg && (\n
\n {this.state.errorMsg}\n \n )}\n
\n \n Remove Bot(s)\n ,\n ]\n : undefined\n }\n checkboxSelectedRows={\n canAssign ? this.state.selectedBots.toJS() : undefined\n }\n bulkActionWorking={this.props.isUpdating}\n data={this.formatData()}\n onClickNodeView={this.handleBotNav}\n customColumnConfig={{\n srn: {\n hide: true,\n },\n description: {\n flex: 1,\n },\n cloud: {\n cellRendererFramework: ({ value }) => {\n if (!value || typeof value !== 'string') {\n return value\n }\n\n return (\n \n {value}\n \n )\n },\n },\n botAssignmentSrn: {\n hide: true,\n },\n }}\n hasCheckBoxes={canAssign}\n selectionChanged={this.handleSelectionChanged}\n />\n
\n
\n Select Bot(s)\n {this.getModalBody()}\n \n \n Cancel\n \n \n \n \n \n \n
\n )\n }\n}\n\nSwimlaneBots.propTypes = {\n isUpdating: PropTypes.bool,\n getBots: PropTypes.func,\n setError: PropTypes.func,\n updateBotAssignments: PropTypes.func,\n error: PropTypes.string,\n allBots: ImmutablePropTypes.map,\n updatingSwimlanes: ImmutablePropTypes.map,\n userHasPermission: PropTypes.func.isRequired,\n swimlane: ImmutablePropTypes.map,\n theme: themeShape,\n push: PropTypes.func,\n updateSwimlane: PropTypes.func,\n accounts: ImmutablePropTypes.iterable,\n}\n\nconst mapStateToProps = createStructuredSelector({\n isUpdating: selectIsUpdatingBotAssignment,\n error: selectSwimlaneBotError,\n updatingSwimlanes: selectUpdatingSwimlanes,\n allBots: selectBots,\n})\n\nfunction mapDispatchToProps(dispatch) {\n return bindActionCreators(\n {\n push,\n updateBotAssignments,\n updateSwimlane,\n getBots,\n setError,\n },\n dispatch\n )\n}\n\nconst withConnect = connect(mapStateToProps, mapDispatchToProps)\n\nexport default compose(withConnect, permissionChecker)(themeable(SwimlaneBots))\n","import React from 'react'\nimport PropTypes from 'prop-types'\nimport ImmutablePropTypes from 'react-immutable-proptypes'\nimport { connect } from 'react-redux'\nimport { createStructuredSelector } from 'reselect'\nimport { compose, bindActionCreators } from 'redux'\n\nimport themeable, { themeShape } from 'containers/ThemeManager/Themeable'\nimport {\n selectSonraiUsers,\n selectAllRoles,\n} from 'containers/SonraiData/selectors'\nimport { selectAssigningUsers } from './selectors'\nimport { updateUserInSwimlane } from './actions'\n\nimport { List } from 'immutable'\nimport WithPermission from 'containers/PermissionChecker/WithPermission'\nimport Button from 'components/Button'\nimport TextLink from 'components/TextLink'\nimport { Modal, ModalBody, ModalHeader } from 'reactstrap'\nimport SelectBar from 'components/SelectBar'\n\nexport class AddAssignmentModal extends React.Component {\n constructor(props) {\n super(props)\n this.state = {\n usersSelected: [],\n rolesSelected: [],\n }\n }\n\n changeUsersSelected = users => {\n this.setState({ usersSelected: users })\n }\n\n changeRolesSelected = roles => {\n this.setState({ rolesSelected: roles })\n }\n\n assignUsers = () => {\n const assignUs = []\n this.state.usersSelected.forEach(user => {\n this.state.rolesSelected.forEach(role => {\n assignUs.push({\n roleSrn: role.value,\n userSrn: user.value,\n scope: this.props.swimlaneResourceId,\n })\n })\n })\n this.props.toggle()\n this.props.updateUserInSwimlane({ create: assignUs })\n }\n\n getValidityMessage = () => {\n const assignedRoles = this.props.sonraiUsers.map(user => {\n return user\n .getIn(['roleAssignments', 'items'], List())\n .filter(item => item.get('scope') === this.props.swimlaneResourceId)\n .map(roleAssignment =>\n roleAssignment.getIn(['role', 'items', 0, 'srn'])\n )\n })\n\n let message = null\n\n if (this.state.usersSelected && this.state.rolesSelected) {\n this.state.usersSelected.forEach(userOpt => {\n const userSrn = userOpt.value\n this.state.rolesSelected.forEach(roleOpt => {\n if (\n assignedRoles.get(userSrn) &&\n assignedRoles.get(userSrn).includes(roleOpt.value)\n ) {\n message = `User ${userOpt.label} already has role ${roleOpt.label} for this swimlane`\n }\n })\n })\n }\n\n return message\n }\n\n getUserModalBody = () => {\n const roles = this.props.allRoles.get('data').map(role => ({\n label: role.get('name'),\n value: role.get('srn'),\n }))\n\n const users = this.props.sonraiUsers\n .toList()\n .map(user => ({ label: user.get('name'), value: user.get('srn') }))\n\n const invalidMessage = this.getValidityMessage()\n let disabled = !!invalidMessage\n\n if (!this.state.rolesSelected || this.state.rolesSelected.length < 1) {\n if (!this.state.usersSelected || !this.state.usersSelected.length < 1) {\n if (this.props.assigningUsers) {\n disabled = true\n }\n }\n }\n\n return (\n
\n
\n Select User(s)\n \n
\n
\n Select Role(s)\n
\n {invalidMessage && (\n
\n {invalidMessage}\n
\n )}\n
\n
\n
\n Cancel\n \n
\n \n \n \n
\n
\n
\n )\n }\n\n render() {\n return (\n
\n Select User(s) and Role(s)\n {this.getUserModalBody()}\n \n )\n }\n}\n\nAddAssignmentModal.propTypes = {\n sonraiUsers: ImmutablePropTypes.map,\n allRoles: ImmutablePropTypes.map,\n assigningUsers: PropTypes.bool,\n swimlaneResourceId: PropTypes.string,\n theme: themeShape,\n toggle: PropTypes.func.isRequired,\n updateUserInSwimlane: PropTypes.func,\n}\n\nconst mapStateToProps = createStructuredSelector({\n sonraiUsers: selectSonraiUsers,\n allRoles: selectAllRoles,\n assigningUsers: selectAssigningUsers,\n})\n\nfunction mapDispatchToProps(dispatch) {\n return bindActionCreators(\n {\n updateUserInSwimlane,\n },\n dispatch\n )\n}\n\nconst withConnect = connect(mapStateToProps, mapDispatchToProps)\n\nexport default compose(withConnect, themeable)(AddAssignmentModal)\n","import React from 'react'\nimport PropTypes from 'prop-types'\nimport ImmutablePropTypes from 'react-immutable-proptypes'\nimport { connect } from 'react-redux'\nimport { createStructuredSelector } from 'reselect'\nimport { compose, bindActionCreators } from 'redux'\nimport { push } from 'connected-react-router'\n\nimport {\n selectSonraiUsers,\n selectSonraiUsersLoading,\n} from 'containers/SonraiData/selectors'\nimport { selectAssigningUsers } from './selectors'\nimport { updateUserInSwimlane } from './actions'\n\nimport { List, fromJS } from 'immutable'\nimport WithPermission from 'containers/PermissionChecker/WithPermission'\nimport Button from 'components/Button'\nimport DataTable from 'components/DataTable'\nimport { ToolbarItem } from 'components/BulkActionToolbar'\nimport SectionHeader from 'components/SectionHeader'\nimport Icon from 'components/Icon'\nimport qs from 'query-string'\nimport _ from 'lodash'\nimport AddAssignmentModal from './AddAssignmentModal'\n\nexport class SwimlaneUsers extends React.Component {\n constructor(props) {\n super(props)\n this.state = {\n selectedUserAssignmentSrns: List(),\n userRoleModalOpen: false,\n }\n this.styles = {\n swimlaneTitleWrapper: {\n display: 'grid',\n gridTemplateColumns: '1fr auto',\n },\n tabTitle: {\n fontSize: '1.2em',\n fontWeight: '400',\n },\n body: {\n marginTop: '1em',\n height: '100%',\n },\n }\n }\n\n toggleUserRoleModal = () => {\n this.setState(prevState => ({\n userRoleModalOpen: !prevState.userRoleModalOpen,\n }))\n }\n\n closeUserRoleModal = () => {\n this.setState({ userRoleModalOpen: false })\n }\n\n handleUserSelectionChange = event => {\n const rows = event.api.getSelectedRows() || []\n const selectedUserAssignmentSrns = []\n rows.forEach(row => {\n selectedUserAssignmentSrns.push({\n assignmentSrn: row.assignmentSrn,\n userSrn: row.userSrn,\n })\n })\n this.setState({\n selectedUserAssignmentSrns: fromJS(selectedUserAssignmentSrns),\n })\n }\n\n handleUserNav = srn => {\n if (srn) {\n this.props.push({\n pathname: '/App/UserManagement/User',\n search: qs.stringify({\n srn,\n }),\n })\n }\n }\n\n unassignUsers = () => {\n this.props.updateUserInSwimlane({\n delete: this.state.selectedUserAssignmentSrns.toJS(),\n })\n this.setState({ selectedUserAssignmentSrns: List() })\n }\n\n render() {\n if (this.props.usersLoading) {\n return null\n }\n\n const users = this.props.sonraiUsers\n .toList()\n .map(user => {\n if (\n user\n .getIn(['roleAssignments', 'items'], List())\n .findIndex(\n item => item.get('scope') === this.props.swimlaneResourceId\n ) !== -1\n ) {\n return user\n } else {\n return null\n }\n })\n .filter(item => !!item)\n .toJS()\n\n const data = []\n users.forEach(user => {\n user.roleAssignments.items.forEach(item => {\n if (item.scope === this.props.swimlaneResourceId) {\n data.push({\n name: user.name,\n email: user.email,\n role: _.get(item, ['role', 'items', 0, 'name'], ''),\n userSrn: user.srn,\n assignmentSrn: item.srn,\n })\n }\n })\n })\n\n return (\n
\n
\n Users Scoped to Swimlane\n \n \n \n
\n
\n \n Unassign User(s)\n ,\n ]}\n checkboxSelectedRows={this.state.selectedUserAssignmentSrns.toJS()}\n data={data}\n hiddenColumns={['assignmentSrn', 'userSrn']}\n multiRowSelect\n hasCheckBoxes\n onClickNodeView={this.handleUserNav}\n selectionChanged={this.handleUserSelectionChange}\n />\n
\n\n {this.state.userRoleModalOpen && (\n
\n )}\n
\n )\n }\n}\n\nSwimlaneUsers.propTypes = {\n sonraiUsers: ImmutablePropTypes.map,\n usersLoading: PropTypes.bool,\n assigningUsers: PropTypes.bool,\n push: PropTypes.func,\n swimlaneResourceId: PropTypes.string,\n updateUserInSwimlane: PropTypes.func,\n}\n\nconst mapStateToProps = createStructuredSelector({\n sonraiUsers: selectSonraiUsers,\n usersLoading: selectSonraiUsersLoading,\n assigningUsers: selectAssigningUsers,\n})\n\nfunction mapDispatchToProps(dispatch) {\n return bindActionCreators(\n {\n push,\n updateUserInSwimlane,\n },\n dispatch\n )\n}\n\nconst withConnect = connect(mapStateToProps, mapDispatchToProps)\n\nexport default compose(withConnect)(SwimlaneUsers)\n","import React, { Fragment, useState } from 'react'\nimport PropTypes from 'prop-types'\nimport ImmutableProptypes from 'react-immutable-proptypes'\nimport CreatableSelect from 'react-select/creatable'\nimport Select from 'react-select'\nimport Button from 'components/Button'\nimport Icon from 'components/Icon'\nimport {\n slackActionTypeKey,\n slackActionTypes,\n} from 'containers/PlatformSettingsData/constants'\n\nimport themeable, { themeShape } from 'containers/ThemeManager/Themeable'\nimport _ from 'lodash'\n\nconst componentsForCreatable = {\n DropdownIndicator: null,\n}\n\nconst createOption = label => ({\n label,\n value: label,\n})\nconst getOptions = things =>\n things.map(thing => ({\n label: slackActionTypeKey[thing] ? slackActionTypeKey[thing] : thing,\n value: thing,\n }))\n\nconst compareValues = (original, current) => {\n let isDifferent = false\n for (let o of original) {\n if (!current.includes(o)) {\n isDifferent = true\n }\n }\n for (let c of current) {\n if (!original.includes(c)) {\n isDifferent = true\n }\n }\n return isDifferent\n}\n\nconst SlackInputs = props => {\n const [triggerValue, setTriggerValue] = useState(\n getOptions(props.actionTypes.toJS())\n )\n const [channelValue, setChannelValue] = useState(\n getOptions(props.channels.toJS())\n )\n const [channelTextValue, setChannelTextValue] = useState('')\n\n const handleKeyDown = event => {\n if (!channelTextValue) return\n switch (event.key) {\n case 'Enter':\n case 'Tab':\n setChannelTextValue('')\n setChannelValue([...channelValue, createOption(channelTextValue)])\n event.preventDefault()\n }\n }\n\n const isDisabled = () => {\n const hasChanges =\n compareValues(\n triggerValue.map(type => type.value),\n props.actionTypes.toJS()\n ) ||\n compareValues(\n channelValue.map(channel => channel.value),\n props.channels.toJS()\n )\n const isEmpty = _.isEmpty(channelValue) || _.isEmpty(triggerValue)\n if (!isEmpty && hasChanges && !props.loading && !props.deleting) {\n return false\n }\n return true\n }\n\n return (\n
\n \n
On Trigger(s)
\n
\n \n
Notify Channel(s)
\n
\n !channels ? setChannelValue([]) : setChannelValue(channels)\n }\n onInputChange={setChannelTextValue}\n menuIsOpen={false}\n inputValue={channelTextValue}\n isClearable={false}\n onKeyDown={handleKeyDown}\n value={channelValue}\n placeholder=\"Enter valid Slack Channel(s) and hit enter...\"\n isMulti\n isDisabled={props.loading || props.deleting}\n />\n {_.isEmpty(channelValue) && (\n \n You must have at least ONE Channel.\n
\n )}\n \n\n \n \n
\n \n )\n}\n\nSlackInputs.propTypes = {\n theme: themeShape,\n actionTypes: ImmutableProptypes.list,\n channels: ImmutableProptypes.list,\n saveValues: PropTypes.func,\n loading: PropTypes.bool,\n deleting: PropTypes.bool,\n}\n\nexport default themeable(SlackInputs)\n","import React, { Fragment, useState } from 'react'\nimport PropTypes from 'prop-types'\nimport Button from 'components/Button'\nimport Icon from 'components/Icon'\nimport { Input } from 'reactstrap'\nimport BorderlessButton from 'components/BorderlessButton'\n\nconst JiraInputs = props => {\n const [projectKey, setProjectKey] = useState(props.projectKey)\n const [putLabels, setPutLabels] = useState(props.putLabels)\n\n const compareValues = () => {\n return projectKey === props.projectKey && putLabels === props.putLabels\n }\n\n return (\n
\n \n
Project Key
\n
setProjectKey(params.target.value)}\n disabled={props.loading || props.deleting}\n />\n
\n \n\n \n \n
\n \n )\n}\n\nJiraInputs.propTypes = {\n saveValues: PropTypes.func,\n loading: PropTypes.bool,\n deleting: PropTypes.bool,\n projectKey: PropTypes.string,\n putLabels: PropTypes.bool,\n}\n\nexport default JiraInputs\n","import { defineMessages } from 'react-intl'\n\nexport default defineMessages({\n AssignmentWarning: {\n id: 'app.containers.UserManagement.assignmentWarning',\n defaultMessage:\n 'Please note that adding a destination for a swimlane could result in the creation of thousands of notifications or tickets in the integrated platform.',\n },\n})\n","import React, { Fragment } from 'react'\nimport PropTypes from 'prop-types'\nimport ImmutablePropTypes from 'react-immutable-proptypes'\nimport { List, Map } from 'immutable'\nimport { connect } from 'react-redux'\nimport { createStructuredSelector } from 'reselect'\nimport { bindActionCreators } from 'redux'\nimport FormLabel from 'components/FormLabel'\nimport {\n selectIntegrations,\n selectIntegrationsLoading,\n selectDeletingIntegrationAssignments,\n selectAssigningIntegrations,\n selectUpdatingConfigs,\n} from 'containers/PlatformSettingsData/selectors'\n\nimport {\n getIntegrations,\n assignIntegration,\n deleteIntegrationAssignment,\n updateIntegrationConfig,\n} from 'containers/PlatformSettingsData/actions'\nimport SquareLoadingAnimation from 'components/SquareLoadingAnimation'\nimport BorderedCard from 'components/BorderedCard'\nimport Button from 'components/Button'\nimport BorderlessButton from 'components/BorderlessButton'\nimport TextLink from 'components/TextLink'\nimport Icon from 'components/Icon'\nimport {\n Modal,\n ModalHeader,\n ModalBody,\n ModalFooter,\n Input,\n Alert,\n} from 'reactstrap'\nimport Select from 'react-select'\nimport CreatableSelect from 'react-select/creatable'\nimport { get, startCase } from 'lodash'\nimport SlackInputs from './SlackInputs'\nimport JiraInputs from './JiraInputs'\nimport messages from './messages'\nimport { slackActionTypes } from 'containers/PlatformSettingsData/constants'\n\nexport class SwimlaneIntegrations extends React.Component {\n constructor(props) {\n super(props)\n if (props.integrations.isEmpty()) {\n props.getIntegrations()\n }\n this.state = {\n isAddModalOpen: false,\n selectedIntegration: null,\n slackChannels: [],\n slackChannelText: '',\n slackTriggers: [],\n slackInputs: Map(),\n jiraPutLabels: true,\n jiraProjectKey: '',\n }\n }\n\n styles = {\n container: {\n display: 'grid',\n gridTemplateRows: 'auto 1fr',\n rowGap: '1em',\n height: '100%',\n },\n cardBox: {\n overflowY: 'auto',\n height: '100%',\n },\n topGuy: {\n display: 'grid',\n gridTemplateColumns: '1fr auto',\n },\n cardTop: {\n display: 'grid',\n gridTemplateColumns: 'auto 1fr auto',\n },\n slackMid: {\n display: 'grid',\n gridTemplateColumns: '1fr 1fr',\n columnGap: '3em',\n },\n title: {\n fontSize: '1.2em',\n },\n mainTitle: {\n fontSize: '1.5em',\n },\n formLabel: {},\n formTemplate: {\n display: 'grid',\n rowGap: '1em',\n },\n }\n\n setJiraPutLabels = value => {\n this.setState({ jiraPutLabels: value })\n }\n\n handleJiraProjectKeyChange = params => {\n this.setState({ jiraProjectKey: params.target.value })\n }\n\n handleSlackActionTypeChange = params => {\n this.setState({ slackTriggers: params })\n }\n\n handleChangeChannel = value => {\n if (!value) {\n this.setState({ slackChannels: [] })\n } else {\n this.setState({ slackChannels: value })\n }\n }\n\n handleChangeChannelText = value => {\n this.setState({ slackChannelText: value })\n }\n createOption = label => ({\n label,\n value: label,\n })\n\n handleKeyDown = event => {\n const { slackChannelText, slackChannels } = this.state\n if (!slackChannelText) return\n switch (event.key) {\n case 'Enter':\n case 'Tab':\n this.setState({\n slackChannelText: '',\n slackChannels: [\n ...slackChannels,\n this.createOption(slackChannelText),\n ],\n })\n event.preventDefault()\n }\n }\n\n componentsForCreatable = {\n DropdownIndicator: null,\n }\n\n openAddModal = () => this.setState({ isAddModalOpen: true })\n\n closeAddModal = () =>\n this.setState({\n isAddModalOpen: false,\n selectedIntegration: null,\n slackChannels: [],\n slackChannelText: '',\n slackTriggers: [],\n })\n\n handleIntegrationChange = params => {\n this.setState({\n selectedIntegration: params,\n slackChannels: [],\n slackChannelText: '',\n slackTriggers: [],\n })\n }\n\n getIntegrationsForSwombo = () => {\n return this.props.integrations.filter(int => {\n if (int.get('configs')) {\n let assignments = List()\n int.get('configs').forEach(config => {\n if (config.get('assignment')) {\n assignments = assignments.merge(config.get('assignment'))\n }\n })\n if (\n assignments.find(\n ass => ass.get('SwimlaneSRN') === this.props.swimlaneSrn\n )\n ) {\n return true\n }\n }\n return false\n })\n }\n\n deleteAssignment = (intSrn, configSrn) => {\n this.props.deleteIntegrationAssignment({ intSrn, assignmentSrn: configSrn })\n }\n\n renderAddModal = () => {\n const options = this.getAvailableIntegrations()\n .map(int => ({\n label: `${int.get('title')} (${startCase(\n int.get('type').toLowerCase()\n )})`,\n value: int,\n }))\n .toJS()\n\n const type = get(this.state, ['selectedIntegration', 'value', 'type'], null)\n ? startCase(\n get(\n this.state,\n ['selectedIntegration', 'value', 'type'],\n ''\n ).toLowerCase()\n )\n : null\n\n return (\n
\n
\n
\n \n {messages.AssignmentWarning.defaultMessage}\n
\n \n
Select Integration
\n
\n
\n {type === 'Slack' ? (\n
\n \n \n \n
\n \n \n\n \n
\n \n ) : (\n type === 'Jira' && (\n
\n \n \n \n )\n )}\n
\n )\n }\n\n updateConfig = (intSrn, srn, params) => {\n this.props.updateIntegrationConfig({\n srn,\n intSrn,\n slack: { actionTypes: params.actionTypes, channels: params.channels },\n type: 'SLACK',\n })\n }\n\n updateJiraConfig = (intSrn, srn, params) => {\n this.props.updateIntegrationConfig({\n srn,\n intSrn,\n jira: {\n ProjectKey: params.projectKey,\n PutLabels: params.putLabels,\n },\n type: 'JIRA',\n })\n }\n\n getSlackCard = (int, index) => {\n const config = int\n .getIn(['configs'], List())\n .find(\n config =>\n config.getIn(['assignment', 0, 'SwimlaneSRN']) ===\n this.props.swimlaneSrn\n )\n\n return (\n
\n
\n \n
\n \n
\n
{`\"${int.get('title')}\" ${startCase(\n int.get('type').toLowerCase()\n )} Workspace`}
\n
\n this.deleteAssignment(int.get('srn'), config.get('srn'))\n }\n disabled={this.props.deletingAssignments.get(config.get('srn'))}\n >\n \n \n
\n \n \n this.updateConfig(int.get('srn'), config.get('srn'), params)\n }\n loading={this.props.updatingConfigs.get(config.get('srn'))}\n deleting={this.props.deletingAssignments.get(config.get('srn'))}\n />\n
\n \n
\n )\n }\n\n getJiraCard = (int, index) => {\n const config = int\n .getIn(['configs'], List())\n .find(\n config =>\n config.getIn(['assignment', 0, 'SwimlaneSRN']) ===\n this.props.swimlaneSrn\n )\n\n return (\n
\n
\n \n
\n \n
\n
{`\"${int.get('title')}\" ${startCase(\n int.get('type').toLowerCase()\n )} Project`}
\n
\n this.deleteAssignment(int.get('srn'), config.get('srn'))\n }\n disabled={this.props.deletingAssignments.get(config.get('srn'))}\n >\n \n \n
\n \n \n this.updateJiraConfig(int.get('srn'), config.get('srn'), params)\n }\n loading={this.props.updatingConfigs.get(config.get('srn'))}\n deleting={this.props.deletingAssignments.get(config.get('srn'))}\n putLabels={config.getIn(['jira', 'PutLabels'])}\n projectKey={config.getIn(['jira', 'ProjectKey'])}\n />\n
\n \n
\n )\n }\n\n getServiceNowCard = (int, index) => {\n const config = int\n .getIn(['configs'], List())\n .find(\n config =>\n config.getIn(['assignment', 0, 'SwimlaneSRN']) ===\n this.props.swimlaneSrn\n )\n\n return (\n
\n
\n \n
\n \n
\n
{`\"${int.get('title')}\" ${startCase(\n int.get('type').toLowerCase()\n )} Platform`}
\n
\n this.deleteAssignment(int.get('srn'), config.get('srn'))\n }\n >\n \n \n
\n \n
\n )\n }\n\n getAvailableIntegrations = () => {\n return this.props.integrations.filter(int => {\n if (!int.get('configs') || int.get('configs').isEmpty()) {\n return true\n }\n if (\n !int.get('configs').find(config => {\n return !config.get('assignment').find(ass => {\n return ass.get('SwimlaneSRN') === this.props.swimlaneSrn\n })\n })\n ) {\n return false\n } else {\n return true\n }\n })\n }\n\n assignIntegration = () => {\n if (this.state.selectedIntegration) {\n this.props.assignIntegration({\n swimlaneSrn: this.props.swimlaneSrn,\n integrationSrn: this.state.selectedIntegration.value.srn,\n slack: {\n actionTypes: this.state.slackTriggers.map(trig => trig.value),\n channels: this.state.slackChannels.map(chan => chan.value),\n },\n jira: {\n ProjectKey: this.state.jiraProjectKey,\n PutLabels: this.state.jiraPutLabels,\n },\n type: this.state.selectedIntegration.value.type,\n })\n this.closeAddModal()\n }\n }\n\n getIsAddDisabled = () => {\n if (\n this.state.selectedIntegration &&\n this.state.selectedIntegration.value.type === 'SERVICE_NOW'\n ) {\n return false\n }\n if (\n this.state.selectedIntegration &&\n this.state.selectedIntegration.value.type === 'JIRA'\n ) {\n if (!this.state.jiraProjectKey) {\n return true\n } else {\n return false\n }\n }\n if (\n !this.state.selectedIntegration ||\n !this.state.slackChannels ||\n !this.state.slackTriggers ||\n this.state.slackChannels.length < 1 ||\n this.state.slackTriggers.length < 1\n ) {\n return true\n } else {\n return false\n }\n }\n\n render() {\n if (this.props.loading) {\n return (\n
\n \n
\n )\n }\n\n const theseInts = this.getIntegrationsForSwombo()\n\n return (\n
\n
\n
Integration Settings
\n
\n \n
\n
\n
\n {theseInts.map((int, index) =>\n int.get('type') === 'SLACK'\n ? this.getSlackCard(int, index)\n : int.get('type') === 'SERVICE_NOW'\n ? this.getServiceNowCard(int, index)\n : int.get('type') === 'JIRA' && this.getJiraCard(int, index)\n )}\n
\n {this.state.isAddModalOpen && (\n
\n Add Destination For Swimlane\n {this.renderAddModal()}\n \n \n Close\n \n \n \n \n )}\n
\n )\n }\n}\n\nSwimlaneIntegrations.propTypes = {\n getIntegrations: PropTypes.func,\n deleteIntegrationAssignment: PropTypes.func,\n assignIntegration: PropTypes.func,\n updateIntegrationConfig: PropTypes.func,\n swimlaneSrn: PropTypes.string,\n integrations: ImmutablePropTypes.list,\n deletingAssignments: ImmutablePropTypes.map,\n updatingConfigs: ImmutablePropTypes.map,\n assigningIntegrations: ImmutablePropTypes.map,\n loading: PropTypes.bool,\n}\n\nconst mapStateToProps = createStructuredSelector({\n integrations: selectIntegrations,\n loading: selectIntegrationsLoading,\n deletingAssignments: selectDeletingIntegrationAssignments,\n assigningIntegrations: selectAssigningIntegrations,\n updatingConfigs: selectUpdatingConfigs,\n})\n\nfunction mapDispatchToProps(dispatch) {\n return bindActionCreators(\n {\n getIntegrations,\n assignIntegration,\n deleteIntegrationAssignment,\n updateIntegrationConfig,\n },\n dispatch\n )\n}\n\nconst withConnect = connect(mapStateToProps, mapDispatchToProps)\n\nexport default withConnect(SwimlaneIntegrations)\n","import React, { Component } from 'react'\nimport PropTypes from 'prop-types'\nimport ImmutablePropTypes from 'react-immutable-proptypes'\nimport qs from 'query-string'\nimport SquareLoadingAnimation from 'components/SquareLoadingAnimation'\nimport TextLink from 'components/TextLink'\nimport { Map } from 'immutable'\nimport DataTable from 'components/DataTable'\nimport CreatedByBadge from 'components/CreatedByBadge'\nimport Button from 'components/Button'\nimport Icon from 'components/Icon'\nimport {\n Modal,\n ModalHeader,\n ModalBody,\n ModalFooter,\n Alert,\n Badge,\n} from 'reactstrap'\nimport SelectBar from 'components/SelectBar'\n\nclass SwimlaneControlFrameworks extends Component {\n constructor(props) {\n super(props)\n this.state = {\n manageModalOpen: false,\n cfSelection: [],\n propCfSelection: [],\n errorMsg: null,\n }\n this.styles = {\n container: {\n display: 'grid',\n gridTemplateRows: 'auto 1fr',\n height: '100%',\n gridRowGap: '1em',\n },\n topGuy: {\n display: 'grid',\n gridTemplateColumns: '1fr auto',\n },\n mainTitle: {\n fontSize: '1.5em',\n },\n }\n }\n\n componentDidUpdate(prevProps) {\n if (\n prevProps.togglingCFs &&\n !this.props.togglingCFs &&\n !this.props.toggleError\n ) {\n this.setState({ cfSelection: [], propCfSelection: [] })\n this.closeManageModal()\n }\n\n if (\n !prevProps.toggleError &&\n this.props.toggleError &&\n !this.state.errorMsg\n ) {\n this.setState({ errorMsg: this.props.toggleError })\n this.props.clearError({ srn: this.props.swimlane.get('srn') })\n this.errorCountDown()\n }\n }\n\n errorCountDown = () => {\n setTimeout(() => {\n this.setState({ errorMsg: null })\n }, 5000)\n }\n\n openManageModal = frameworks => {\n const selection = frameworks\n .toList()\n .filter(\n cf =>\n cf.get('swimlaneSRNs') &&\n cf.get('swimlaneSRNs').includes(this.props.swimlane.get('srn'))\n )\n .map(cf => ({ value: cf.get('srn'), label: cf.get('title') }))\n\n this.setState({\n cfSelection: selection,\n propCfSelection: selection,\n })\n this.setState({ manageModalOpen: true })\n }\n\n closeManageModal = () => {\n this.setState({ manageModalOpen: false })\n }\n\n getData = frameworks => {\n return frameworks\n .toList()\n .map(framework => ({\n title: framework.get('title') || '-',\n description: framework.get('description') || '-',\n createdBy: framework.get('createdBy'),\n lastModified: framework.get('lastModified') || '-',\n srn: framework.get('srn'),\n scope: {\n global:\n framework.get('swimlaneSRNs') &&\n framework\n .get('swimlaneSRNs')\n .contains(this.props.swimlane.get('srn'))\n ? false\n : true,\n },\n }))\n .toJS()\n }\n\n updateCfSelection = params => {\n if (!params) {\n this.setState({ cfSelection: [] })\n } else {\n this.setState({ cfSelection: params })\n }\n }\n\n renderManageModal = () => {\n const options = this.props.controlGroups\n .toList()\n .filter(\n cf =>\n cf.get('createdBy') &&\n !cf.get('createdBy').includes('supersonrai') &&\n cf.get('enabled')\n )\n .toJS()\n .map(cf => ({ value: cf.srn, label: cf.title }))\n\n return (\n
\n
Select Control Framework(s)
\n
\n
\n )\n }\n\n getIsDiffSelection = () => {\n let diff = false\n this.state.propCfSelection.forEach(sel => {\n if (\n this.state.cfSelection.findIndex(cf => cf.value === sel.value) === -1\n ) {\n diff = true\n }\n })\n this.state.cfSelection.forEach(sel => {\n if (\n this.state.propCfSelection.findIndex(cf => cf.value === sel.value) ===\n -1\n ) {\n diff = true\n }\n })\n return diff\n }\n\n applySwimlaneToCFs = () => {\n if (this.getIsDiffSelection()) {\n const add = []\n const remove = []\n this.state.propCfSelection.forEach(sel => {\n if (\n this.state.cfSelection.findIndex(cf => cf.value === sel.value) === -1\n ) {\n remove.push(sel.value)\n }\n })\n this.state.cfSelection.forEach(sel => {\n if (\n this.state.propCfSelection.findIndex(cf => cf.value === sel.value) ===\n -1\n ) {\n add.push(sel.value)\n }\n })\n\n this.props.toggleSwimlaneOnCFs({\n srn: this.props.swimlane.get('srn'),\n add,\n remove,\n })\n }\n }\n\n renderTable = frameworks => {\n if (!this.props.isLoaded) {\n return (\n
\n \n
\n )\n }\n\n return (\n
{\n if (!params.data) {\n return null\n }\n return (\n \n {params.value.global ? 'Global' : 'Applied'}\n \n )\n },\n },\n title: {\n minWidth: 300,\n cellRendererFramework: ({ data }) => {\n if (!data) {\n return\n }\n const { srn, title } = data\n return (\n \n {title}\n \n )\n },\n },\n createdBy: {\n cellRendererFramework: ({ value, data }) => {\n if (!data) {\n return\n }\n\n return \n },\n },\n }}\n />\n )\n }\n\n render() {\n const allFrameworks = this.props.controlGroups || Map()\n\n const frameworks = allFrameworks.filter(item => {\n return (\n (!item.get('swimlaneSRNs') && item.get('enabled')) ||\n (item.get('swimlaneSRNs') &&\n item.get('swimlaneSRNs').includes(this.props.swimlane.get('srn')))\n )\n })\n return (\n \n
\n
\n Control Frameworks Scoped To Swimlane\n
\n
\n \n
\n
\n {this.renderTable(frameworks)}\n {this.state.manageModalOpen && (\n
\n \n {this.state.errorMsg && (\n {this.state.errorMsg}\n )}\n Manage Applied Control Frameworks on Swimlane
\n \n {this.renderManageModal()}\n \n \n Close\n \n \n \n \n )}\n
\n )\n }\n}\n\nSwimlaneControlFrameworks.propTypes = {\n controlGroups: ImmutablePropTypes.map,\n swimlane: ImmutablePropTypes.map,\n isLoaded: PropTypes.bool,\n toggleSwimlaneOnCFs: PropTypes.func,\n togglingCFs: PropTypes.bool,\n toggleError: PropTypes.string,\n clearError: PropTypes.func,\n}\n\nexport default SwimlaneControlFrameworks\n","import React, { Fragment } from 'react'\nimport ImmutablePropTypes from 'react-immutable-proptypes'\nimport PropTypes from 'prop-types'\nimport { Button, Form, FormGroup, Label } from 'reactstrap'\nimport MultiValueInput from 'components/MultiValueInput'\nimport Icon from 'components/Icon'\nimport TextLink from 'components/TextLink'\nimport { ModalHeader, ModalBody, ModalFooter } from 'reactstrap'\nimport AccountSelector from 'components/AccountSelector'\nimport SwimlaneTagManager from 'containers/SwimlaneManager/SwimlaneTagManager'\nimport _ from 'lodash'\nimport themeable, { themeShape } from 'containers/ThemeManager/Themeable'\n\nexport class EditFilters extends React.Component {\n constructor(props) {\n super(props)\n const swombo = props.swimlane.toJS()\n this.state = {\n swimlaneAccounts: swombo.accounts ? swombo.accounts : [],\n swimlaneTags: swombo.tags ? swombo.tags : [],\n swimlaneNames: swombo.names ? swombo.names : [],\n swimlaneResourceIds: swombo.resourceIds ? swombo.resourceIds : [],\n advanced: true,\n }\n\n this.styles = {\n submit: {\n margin: '1em 0',\n float: 'right',\n },\n modalBody: {\n maxHeight: '70vh',\n overflow: 'auto',\n },\n }\n }\n\n doSave = () => {\n this.props.save({\n srn: this.props.swimlane.get('srn'),\n swimlane: {\n ...this.props.swimlane.toJS(),\n accounts: this.state.swimlaneAccounts,\n tags: this.state.swimlaneTags,\n resourceIds: this.state.swimlaneResourceIds,\n names: this.state.swimlaneNames,\n },\n })\n this.props.toggle()\n }\n\n getTagOptions = () => {\n const serverTags = this.props.tags\n .filter(tag => !!tag.get('key'))\n .sortBy(tag => tag.get('key').toLowerCase())\n .toJS()\n .map(tag => ({ value: tag.key, label: tag.key }))\n\n const additionalOptions = this.state.swimlaneTags.map(tagStr => ({\n value: tagStr,\n label: tagStr,\n }))\n\n return serverTags.concat(additionalOptions)\n }\n\n setSwimlaneTags = newTagsVal => {\n if (!newTagsVal) {\n this.setState({\n swimlaneTags: [],\n })\n } else {\n const { value } = newTagsVal\n if (Array.isArray(value)) {\n this.setState({\n swimlaneTags: value,\n })\n } else {\n let arr = [...this.state.swimlaneTags]\n\n const { value, key, toRemove } = newTagsVal\n const combineStr = key !== '' || value !== '' ? `${key}:${value}` : key\n\n if (toRemove) {\n const index = arr.findIndex(tag => tag.includes(combineStr))\n arr.splice(index, 1)\n this.setState({ swimlaneTags: arr })\n } else {\n this.setState(state => ({\n swimlaneTags: [...state.swimlaneTags, combineStr],\n }))\n }\n }\n }\n }\n\n setSwimlaneResourceIds = values => {\n this.setState({\n swimlaneResourceIds: values,\n })\n }\n\n setSwimlaneAccounts = newAccountsVal => {\n this.setState({\n swimlaneAccounts: newAccountsVal,\n })\n }\n\n setSwimlaneTitle = e => {\n this.setState({\n swimlaneTitle: e.target.value,\n })\n }\n\n setSwimlaneDescription = e => {\n this.setState({\n swimlaneDescription: e.target.value,\n })\n }\n\n setSwimlaneNames = values => {\n this.setState({\n swimlaneNames: values,\n })\n }\n\n renderButtons = nameIsValid => {\n // Buttons should always be positive action on the right,\n // because people are use to things moving to the right as moving forward, and left as backwards\n // shut up nerd\n // lol, good one tim\n\n const {\n swimlaneAccounts,\n swimlaneNames,\n swimlaneTags,\n swimlaneResourceIds,\n } = this.state\n\n const hasNoFilters =\n _.isEmpty(swimlaneAccounts) &&\n _.isEmpty(swimlaneNames) &&\n _.isEmpty(swimlaneTags) &&\n _.isEmpty(swimlaneResourceIds)\n\n if (this.props.updating) {\n return (\n \n )\n }\n\n return (\n \n \n {hasNoFilters && (\n
\n \n \n No resources will match this Swimlane unless at least one filter\n is applied.\n \n
\n )}\n
\n Cancel\n \n
\n
\n \n )\n }\n\n isNameValid = () => {\n const { swimlane, swimlanes } = this.props\n const title = this.state.swimlaneTitle.trim().toLowerCase()\n\n if (title === 'all' || /\\s/g.test(title)) {\n return false\n }\n\n let filteredSwimlanes = swimlanes\n\n if (swimlanes) {\n if (swimlane) {\n filteredSwimlanes = filteredSwimlanes.filter(\n sl => sl.get('srn') !== swimlane.get('srn')\n )\n }\n\n if (\n filteredSwimlanes.find(\n swimlane => swimlane.get('title', '').toLowerCase() === title\n )\n ) {\n return false\n }\n }\n\n return true\n }\n\n render() {\n const selectStyles = {\n menuList: styles => {\n return {\n ...styles,\n maxHeight: '175px',\n }\n },\n }\n\n return (\n \n \n Edit Swimlane Filters\n \n \n \n \n {this.renderButtons(true)} \n \n )\n }\n}\n\nEditFilters.propTypes = {\n theme: themeShape,\n swimlane: ImmutablePropTypes.map,\n accounts: ImmutablePropTypes.iterable,\n swimlanes: ImmutablePropTypes.map,\n save: PropTypes.func,\n toggleModal: PropTypes.func,\n close: PropTypes.func,\n toggle: PropTypes.func,\n fetchTags: PropTypes.func,\n getTagValues: PropTypes.func,\n updating: PropTypes.bool,\n isLoadingTagValues: PropTypes.bool,\n tagValues: ImmutablePropTypes.iterable,\n tags: ImmutablePropTypes.list,\n}\n\nexport default themeable(EditFilters)\n","import React from 'react'\nimport PropTypes from 'prop-types'\nimport ImmutablePropTypes from 'react-immutable-proptypes'\nimport { List } from 'immutable'\nimport { connect } from 'react-redux'\nimport { createStructuredSelector } from 'reselect'\nimport { compose, bindActionCreators } from 'redux'\nimport { push } from 'connected-react-router'\n\nimport { GLOBAL_SWIMLANE_NAME } from 'appConstants'\nimport SectionHeader from 'components/SectionHeader'\nimport injectReducer from 'utils/injectReducer'\nimport injectSaga from 'utils/injectSaga'\n\nimport reducer from './reducer'\nimport sagas from './sagas'\n\nimport {\n selectSwimlanesLoading,\n selectTags,\n selectAccounts,\n selectSwimlanes,\n} from 'containers/SonraiData/selectors'\n\nimport {\n selectControlGroups,\n selectLoadedControlGroups,\n selectUpdatingCFs,\n selectTogglingSwimlaneOnCfs,\n selectTogglingSwimlaneError,\n} from 'containers/ControlFrameworkData/selectors'\nimport {\n toggleSwimlaneOnCFs,\n toggleSwimlaneOnCFsSuccess,\n} from 'containers/ControlFrameworkData/actions'\n\nimport {\n selectUpdatingSwimlanes,\n selectUpdatingSwimlanesError,\n} from './selectors'\n\nimport { removeSwimlane, fetchTags } from 'containers/SonraiData/actions'\nimport { getTagValues, updateSwimlane, setUpdateSwimlaneError } from './actions'\n\nimport WithPermission from 'containers/PermissionChecker/WithPermission'\nimport themeable, { themeShape } from 'containers/ThemeManager/Themeable'\nimport color from 'color'\nimport BorderedCard from 'components/BorderedCard'\nimport { Helmet } from 'react-helmet'\nimport { getNameForHelmet } from 'utils/sonraiUtils'\nimport { Breadcrumb, BreadcrumbItem, Alert } from 'reactstrap'\nimport BorderlessButton from 'components/BorderlessButton'\nimport Button from 'components/Button'\nimport Icon from 'components/Icon'\nimport { NodeViewTabs } from 'components/NodeView'\nimport SquareLoadingAnimation from 'components/SquareLoadingAnimation'\nimport TextLink from 'components/TextLink'\nimport EditDetails from './EditDetails'\nimport SwimlaneBots from './SwimlaneBots'\nimport SwimlaneUsers from './SwimlaneUsers'\nimport SwimlaneIntegrations from './SwimlaneIntegrations'\nimport SwimlaneControlFrameworks from './SwimlaneControlFrameworks'\nimport EditFilters from './EditFilters'\nimport { Modal, ModalBody } from 'reactstrap'\nimport qs from 'query-string'\nimport CreatedByBadge from 'components/CreatedByBadge'\nimport SwimlaneFilterDisplay from './SwimlaneFilterDisplay'\n\nclass SwimlaneDetails extends React.Component {\n constructor(props) {\n super(props)\n\n if (props.tags.isEmpty()) {\n props.fetchTags()\n }\n\n this.state = {\n editOpen: false,\n deletePromptOpen: false,\n editFilterOpen: false,\n }\n }\n\n styles = {\n container: {\n width: '100%',\n height: '100%',\n display: 'grid',\n gridTemplateRows: 'auto 1fr',\n gridRowGap: '0.5em',\n padding: '1rem',\n overflow: 'auto',\n },\n breadcrumbs: {\n fontSize: '1rem',\n margin: '0rem 0rem 0.5rem 0rem',\n },\n swimlaneTitleWrapper: {\n display: 'grid',\n gridTemplateColumns: '1fr auto',\n },\n swimlaneTitle: {\n fontWeight: 400,\n fontSize: '1.5rem',\n },\n swimlaneDescription: {},\n defaultImportance: {\n fontSize: '0.9em',\n margin: '0.25rem 0rem',\n paddingRight: '0.75rem',\n textAlign: 'right',\n },\n empty: {\n fontStyle: 'italic',\n color: this.props.theme.neutralMedium,\n },\n containers: {\n display: 'flex',\n flexDirection: 'column',\n padding: '0.75rem',\n },\n containerTitles: {\n fontSize: '0.95rem',\n textAlign: 'left',\n fontWeight: '400',\n marginBottom: '0.5rem',\n alignSelf: 'flex-start',\n },\n body: {\n height: '100%',\n },\n tabContainer: {\n height: '100%',\n padding: '1rem',\n backgroundColor: this.props.theme.light,\n borderTop: 0,\n borderBottom: `1px solid ${this.props.theme.neutralMedium}`,\n borderLeft: `1px solid ${this.props.theme.neutralMedium}`,\n borderRight: `1px solid ${this.props.theme.neutralMedium}`,\n },\n button: {\n height: '1.85rem',\n fontSize: '0.85rem',\n border: `1px solid ${this.props.theme.primary}`,\n padding: '0.25rem 0.5rem',\n margin: '4px 5px 0px 0px',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'space-between',\n color: color(this.props.theme.neutralDark).darken(0.6),\n },\n actions: {\n display: 'grid',\n gridTemplateColumns: 'auto auto',\n },\n }\n\n toggleEditOpen = () => {\n this.setState(prevState => ({ editOpen: !prevState.editOpen }))\n }\n\n toggleEditFilterOpen = () => {\n this.setState(prevState => ({ editFilterOpen: !prevState.editFilterOpen }))\n }\n\n toggleDeletePromptOpen = () => {\n this.setState(prevState => ({\n deletePromptOpen: !prevState.deletePromptOpen,\n }))\n }\n\n toggleFrameworkModal = () => {\n this.setState(prevState => ({\n frameworkModalOpen: !prevState.frameworkModalOpen,\n }))\n }\n\n closeEdit = () => {\n this.setState({ editOpen: false })\n }\n\n closeDelete = () => {\n this.setState({ deletePromptOpen: false })\n }\n\n closeEditFilter = () => {\n this.setState({ editFilterOpen: false })\n }\n\n saveDetails = params => {\n this.props.updateSwimlane(params)\n this.closeEdit()\n }\n\n isGlobalSwimlane = swimlane => {\n return swimlane.get('title') === GLOBAL_SWIMLANE_NAME\n }\n\n renderTitle = swimlane => {\n if (!swimlane) {\n return (\n \n \n
\n )\n }\n\n let controlsStyle = {}\n if (this.isGlobalSwimlane(swimlane)) {\n controlsStyle.display = 'none'\n }\n\n return (\n \n
{swimlane.get('title')}
\n
\n
\n \n
\n \n \n
\n Edit Swimlane\n \n
\n \n \n
\n Delete Swimlane\n \n
\n \n
\n\n
\n \n \n
\n )\n }\n\n renderDetails = swimlane => {\n return (\n \n
\n
\n \n {swimlane.get('description') || (\n ( No Description )\n )}\n \n
\n
\n Default Importance:{' '}\n \n {swimlane.get('defaultImportance')}\n \n
\n
\n
\n
\n {' '}\n Created By:{' '}\n
\n
\n
\n
\n )\n }\n\n renderFilters = swimlane => {\n const tags = swimlane.get('tags') || List()\n const names = swimlane.get('names') || List()\n const resources = swimlane.get('resourceIds') || List()\n const accounts = swimlane.get('accounts') || List()\n const hasNoFilters =\n tags.isEmpty() &&\n names.isEmpty() &&\n resources.isEmpty() &&\n accounts.isEmpty()\n\n return (\n \n
\n
Swimlane Filters\n {!this.isGlobalSwimlane(swimlane) && (\n
\n \n {hasNoFilters && (\n
\n \n \n No resources will match this Swimlane unless at least one\n filter is applied.\n \n
\n )}\n
\n
\n \n )}\n
\n
\n
\n \n \n
\n )\n }\n\n renderBody = swimlane => {\n const startTab =\n qs.parse(this.props.location.search).tab === 'integrations' ? 3 : 0\n return (\n \n \n
\n {this.renderFilters(swimlane)}\n
\n
\n \n \n \n \n \n )\n }\n\n clearError = srn => {\n this.props.setUpdateSwimlaneError({ srn: srn, error: null })\n }\n\n removeSwimlane = swimlane => {\n this.props.removeSwimlane({\n srn: swimlane.get('srn'),\n title: swimlane.get('title'),\n })\n this.props.push({ pathname: '/App/SwimlaneManagement' })\n }\n\n render() {\n if (this.props.swimlanesLoading) {\n return (\n \n \n
\n )\n }\n\n const srn = qs.parse(this.props.location.search).srn\n if (!srn) {\n this.props.push({ pathname: '/App/SwimlaneManagement' })\n }\n\n if (this.props.swimlanesLoading) {\n return (\n \n \n
\n )\n }\n\n const swimlane = this.props.swimlanes.find(\n swimmi => swimmi.get('srn') === srn\n )\n\n return (\n \n
\n
\n \n \n this.props.push({ pathname: '/App/SwimlaneManagement' })\n }\n >\n All Swimlanes\n \n Swimlane Details\n \n
\n {this.props.updatingError.get(swimlane.get('srn')) && (\n
this.clearError(swimlane.get('srn'))}\n >\n {this.props.updatingError.get(swimlane.get('srn'))}\n \n )}\n
\n {this.renderTitle(swimlane)}\n {this.renderDetails(swimlane)}\n \n
\n\n
{this.renderBody(swimlane)}
\n {this.state.deletePromptOpen && (\n
\n \n Confirm Swimlane Deletion\n \n
\n \n Cancel\n \n \n
\n
\n \n \n )}\n {swimlane && (\n
\n )}\n
\n )\n }\n}\n\nSwimlaneDetails.propTypes = {\n theme: themeShape,\n removeSwimlane: PropTypes.func,\n push: PropTypes.func,\n getTagValues: PropTypes.func,\n fetchTags: PropTypes.func,\n setUpdateSwimlaneError: PropTypes.func,\n updateSwimlane: PropTypes.func,\n updating: ImmutablePropTypes.map,\n updatingError: ImmutablePropTypes.map,\n location: PropTypes.shape({\n search: PropTypes.string,\n }),\n swimlanes: ImmutablePropTypes.map,\n allBots: ImmutablePropTypes.map,\n tags: ImmutablePropTypes.iterable,\n accounts: ImmutablePropTypes.iterable,\n swimlanesLoading: PropTypes.bool,\n tagValues: ImmutablePropTypes.iterable,\n controlGroups: ImmutablePropTypes.iterable,\n controlGroupsLoaded: PropTypes.bool,\n updatingCFs: ImmutablePropTypes.map,\n toggleSwimlaneOnCFs: PropTypes.func,\n toggleSwimlaneOnCFsSuccess: PropTypes.func,\n togglingSwimlaneOnCfs: ImmutablePropTypes.map,\n togglingSwimlaneError: ImmutablePropTypes.map,\n}\n\nconst mapStateToProps = createStructuredSelector({\n swimlanes: selectSwimlanes,\n swimlanesLoading: selectSwimlanesLoading,\n updating: selectUpdatingSwimlanes,\n updatingError: selectUpdatingSwimlanesError,\n tags: selectTags,\n accounts: selectAccounts,\n controlGroups: selectControlGroups,\n controlGroupsLoaded: selectLoadedControlGroups,\n updatingCFs: selectUpdatingCFs,\n togglingSwimlaneOnCfs: selectTogglingSwimlaneOnCfs,\n togglingSwimlaneError: selectTogglingSwimlaneError,\n})\n\nfunction mapDispatchToProps(dispatch) {\n return bindActionCreators(\n {\n push,\n removeSwimlane,\n getTagValues,\n updateSwimlane,\n fetchTags,\n setUpdateSwimlaneError,\n toggleSwimlaneOnCFs,\n toggleSwimlaneOnCFsSuccess,\n },\n dispatch\n )\n}\n\nconst withConnect = connect(mapStateToProps, mapDispatchToProps)\nconst withReducer = injectReducer({ key: 'swimlaneDetails', reducer })\nconst withSaga = injectSaga({ key: 'swimlaneDetails', saga: sagas })\n\nexport default compose(\n withReducer,\n withConnect,\n withSaga,\n themeable\n)(SwimlaneDetails)\n"],"sourceRoot":""}