{ setDialogOpened(false) }}\n content={modalContent}\n buttons={modalButtons}\n windowID=\"new_global_order\"\n />\n >\n )\n}\n\nElementsNewOrderButton.propTypes = {\n locations: PropTypes.array.isRequired,\n}\n\nexport default ElementsNewOrderButton\n","import { connect } from 'react-redux'\nimport { dropToSelected, dropToAvailable, loadSelected } from 'actions/elements_groups_enabled_columns'\n\nfunction mapStateToProps (state) {\n return {\n selectedColumns: state.getIn(['element', 'group', 'enabledColumns', 'selectedColumns']),\n }\n}\n\nconst mapDispatchToProps = { dropToSelected, dropToAvailable, loadSelected }\n\nexport default connect(mapStateToProps, mapDispatchToProps)\n","import React, { Component } from 'react'\nimport PropTypes from 'prop-types'\nimport { DragSource, DropTarget } from 'react-dnd'\n\nconst columnSource = {\n beginDrag (props, monitor, component) {\n return {\n name: props.name,\n }\n },\n endDrag (props, monitor, component) {\n if (!monitor.didDrop()) {\n return\n }\n\n const result = monitor.getDropResult()\n if (result.hasOwnProperty('isSelected') && result.isSelected) {\n props.dropToSelected(props.name, result.position)\n } else {\n props.dropToAvailable(props.name)\n }\n }\n}\n\nfunction collect(connect, monitor) {\n return {\n connectDragSource: connect.dragSource(),\n isDragging: monitor.isDragging()\n }\n}\n\nfunction collectDrop(connect, monitor) {\n return {\n connectDropTarget: connect.dropTarget(),\n isOver: monitor.isOver()\n }\n\n}\n\nconst columnTarget = {\n drop(props, monitor) {\n return {\n name: props.name,\n position: props.position,\n isSelected: props.isSelected()\n }\n },\n}\n\n\nclass Column extends Component {\n static propTypes = {\n name: PropTypes.string.isRequired,\n position: PropTypes.number,\n isSelected: PropTypes.func,\n translate: PropTypes.func.isRequired,\n }\n\n render () {\n const { name, position, translate, connectDragSource, connectDropTarget,\n isOver, isSelected } = this.props\n let columnClass = `enabled-columns-column column-${name}`\n if (isOver && isSelected() ) {\n columnClass += ' drop-target-active'\n }\n return connectDragSource && connectDropTarget && connectDragSource(connectDropTarget(\n \n {translate(name)}\n \n ))\n }\n}\n\nexport default DragSource('enabled-columns', columnSource, collect)(\n DropTarget('enabled-columns', columnTarget, collectDrop)(Column)\n )\n","import React, { Component } from 'react'\nimport PropTypes from 'prop-types'\nimport { List } from 'immutable'\nimport { DropTarget } from 'react-dnd';\nimport Column from './column'\n\nconst I18n = window.I18n\n\nconst selectedColumnsTarget = {\n drop(props, monitor, component) {\n // do not return anything if other component already handled 'drop'\n if (monitor.didDrop()) {\n return\n }\n\n return {\n isSelected: true\n }\n },\n}\n\nfunction collect(connect, monitor) {\n return {\n connectDropTarget: connect.dropTarget(),\n isOver: monitor.isOver()\n }\n}\n\nclass SelectedColumns extends Component {\n static propTypes = {\n selectedColumns: PropTypes.instanceOf(List).isRequired,\n translate: PropTypes.func.isRequired,\n dropToSelected: PropTypes.func.isRequired,\n dropToAvailable: PropTypes.func.isRequired,\n }\n\n render() {\n const { selectedColumns, connectDropTarget, isOver, translate } = this.props\n let className = 'enabled-columns-selected'\n if (isOver) {\n className += ' drop-target-active'\n }\n\n return connectDropTarget && connectDropTarget(\n \n {this.dropZoneTitle()}\n {selectedColumns.map( (column, position) => {\n return true }\n dropToSelected={this.props.dropToSelected}\n dropToAvailable={this.props.dropToAvailable}\n />\n })}\n
\n )\n }\n\n dropZoneTitle() {\n if (this.props.selectedColumns.size == 0) {\n return (\n \n {I18n.t('elements.groups.form.drop_zone_title')}\n \n )\n }\n }\n}\n\nexport default DropTarget('enabled-columns', selectedColumnsTarget, collect)(SelectedColumns)\n","import React, { Component } from 'react'\nimport PropTypes from 'prop-types'\nimport { List } from 'immutable'\nimport { DropTarget } from 'react-dnd';\nimport Column from './column'\n\nconst availableColumnsTarget = {\n drop(props) {\n return\n }\n}\n\nfunction collect(connect, monitor) {\n return {\n connectDropTarget: connect.dropTarget(),\n isOver: monitor.isOver()\n }\n}\n\nclass AvailableColumns extends Component {\n static propTypes = {\n availableColumns: PropTypes.array.isRequired,\n translate: PropTypes.func.isRequired,\n dropToSelected: PropTypes.func.isRequired,\n dropToAvailable: PropTypes.func.isRequired,\n }\n\n render() {\n const { availableColumns, connectDropTarget, isOver, translate } = this.props\n return connectDropTarget && connectDropTarget(\n \n {availableColumns.map( (name) => {\n return false }\n dropToSelected={this.props.dropToSelected}\n dropToAvailable={this.props.dropToAvailable}\n />\n })}\n
\n )\n }\n}\n\nexport default DropTarget('enabled-columns', availableColumnsTarget, collect)(AvailableColumns)\n","import React, { Component } from 'react'\nimport PropTypes from 'prop-types'\nimport { List } from 'immutable'\nimport { DndProvider } from 'react-dnd'\nimport HTML5Backend from 'react-dnd-html5-backend'\nimport connect from './connect'\nimport SelectedColumns from './selected_columns'\nimport AvailableColumns from './available_columns'\n\nclass EnabledColumns extends Component {\n static propTypes = {\n selectedColumns: PropTypes.instanceOf(List).isRequired,\n allColumns: PropTypes.array.isRequired,\n inputName: PropTypes.string.isRequired,\n values: PropTypes.array\n }\n\n constructor(props) {\n super(props)\n this.translations = {}\n this.props.allColumns.forEach(col => this.translations[col[1]] = col[0])\n }\n componentDidMount() {\n if (this.props.values) {\n this.props.loadSelected(this.props.values)\n }\n }\n\n render() {\n return (\n \n \n
\n\n
\n { this.hiddenInputs() }\n
\n \n )\n }\n\n availableColumns() {\n const columns = this.props.allColumns\n .map( col => col[1] )\n .filter( col => !this.props.selectedColumns.includes(col))\n return columns\n }\n\n translate(name) {\n return this.translations[name]\n }\n\n hiddenInputs() {\n return this.props.selectedColumns.map( (column) => {\n return (\n \n )\n })\n }\n}\n\nexport default connect(EnabledColumns)\n","import { Component } from 'react'\nimport store from 'store'\nimport { Provider } from 'react-redux'\nimport EnabledColumns from './enabled_columns'\n\nexport default class EnabledColumnsApp extends Component {\n render () {\n return (\n \n \n \n )\n }\n}\n","import EnabledColumns from './enabled_columns_app'\n\nexport default { EnabledColumns }\n","import Groups from './groups'\n\nexport default { Groups }\n","import React from 'react'\nimport moment from 'moment'\nimport ReactHover, { Trigger, Hover } from 'react-hover'\n\nconst DAYS_TO_RENDER_BEFORE_TERM = 1\nconst DAYS_TO_RENDER_AFTER_TERM = 3\nconst MINIMAL_DAYS_TO_RENDER = 20\n\n// Helper class for manipulating a calendar abstraction.\nclass CalendarHelper {\n\n constructor (startDate, endDate) {\n this.startDate = moment(startDate)\n this.endDate = moment(endDate)\n }\n\n // Beginning of the calendar\n start () {\n return this.startDate.clone().subtract(DAYS_TO_RENDER_BEFORE_TERM, 'day')\n }\n\n // End of the calendar\n end () {\n const firstDay = this.start()\n const lastDay = this.endDate.clone().add(DAYS_TO_RENDER_AFTER_TERM, 'days')\n\n const termLength = lastDay.diff(firstDay, 'days') + 2\n if (termLength < MINIMAL_DAYS_TO_RENDER) {\n return firstDay.add(MINIMAL_DAYS_TO_RENDER, 'days')\n }\n return lastDay\n }\n\n // Actual amount of days in the calendar\n totalDays () {\n return this.end().diff(this.start(), 'days') + 2\n }\n\n months () {\n const lastMonth = this.end().endOf('month')\n let currentMonth = this.start().startOf('month')\n const months = [currentMonth]\n while (currentMonth.isBefore(lastMonth, 'month')) {\n currentMonth = currentMonth.clone().add(1, 'month')\n months.push(currentMonth)\n }\n\n return months\n }\n\n days () {\n const result = []\n const lastDay = this.end().add(1, 'day')\n let currentDay = this.start()\n\n do {\n result.push(currentDay)\n currentDay = currentDay.clone().add(1, 'day')\n } while (currentDay.isBefore(lastDay))\n\n return result\n }\n\n colspanFor (date) {\n if (this.start().month() === date.month()) {\n return date.daysInMonth() - this.start().date() + 1\n }\n return date.daysInMonth()\n }\n}\n\nfunction CalendarHeadRows (props) {\n const { startDate, endDate } = props\n const cal = new CalendarHelper(startDate, endDate)\n return (\n <>\n \n \n { cal.months().map(m => (\n {m.format('MMM')} | \n )) }\n
\n \n \n { cal.days().map(d => (\n {d.format('DD')} | \n )) }\n
\n \n \n { cal.days().map(d => (\n {d.format('dd')} | \n )) }\n
\n >\n )\n}\n\nfunction CalendarElementColumns (props) {\n const {\n element, startDate, endDate, rentedItems, rentList,\n } = props\n const cal = new CalendarHelper(startDate, endDate)\n const { id } = element\n\n const cellColor = (day) => {\n const NOT_RENTED = 'transparent'\n const RENTED_IN_CURRENT_LIST = 'green'\n const RENTED_IN_OTHER_LIST = 'lightcoral'\n\n if (!rentedItems) { return NOT_RENTED }\n\n const items = rentedItems[id]\n if (!items) { return NOT_RENTED }\n const rentListId = rentList ? rentList.id : 0\n for (const item of items) {\n const list = item.rentList\n\n if (day.isBetween(list.startDate, list.endDate, 'day', '[]')) {\n return list.id == rentListId ? RENTED_IN_CURRENT_LIST : RENTED_IN_OTHER_LIST\n }\n\n }\n return NOT_RENTED\n }\n\n const eventName = (day) => {\n if (!rentedItems) { return }\n\n const items = rentedItems[id]\n if (!items) { return }\n const rentListId = rentList ? rentList.id : 0\n for (const item of items) {\n const list = item.rentList\n\n const end = moment(list.endDate)\n if (day.isBetween(list.startDate, end, 'day', '[]')) {\n if (list.id !== rentListId) {\n return `${list.companyName} - ${list.eventName}`\n }\n return\n }\n }\n }\n\n return (\n <>\n { cal.days().map((d) => {\n const color = cellColor(d)\n if (color === 'transparent') {\n return | \n }\n\n const event = eventName(d)\n if (!event) {\n return | \n }\n\n return (\n \n \n \n \n \n \n \n \n \n { event }\n \n \n \n | \n )\n }) }\n >\n )\n}\n\nfunction FirstColumns () {\n return (\n | \n )\n}\n\n\nexport { CalendarHeadRows, CalendarElementColumns, CalendarHelper }\n","import React from 'react'\nimport PropTypes from 'prop-types'\n\nfunction ElementInfo (props) {\n const { element } = props\n return (\n \n
{element.name}
\n
\n {`${element.sku}, H: ${element.width}, B: ${element.height}, D: ${element.depth}`}\n
\n
\n

\n
\n
\n )\n}\n\nElementInfo.propTypes = {\n element: PropTypes.object.isRequired,\n}\n\nexport default ElementInfo\n","import ReactHover, { Hover, Trigger } from 'react-hover'\nimport React, { Fragment } from 'react'\nimport { CalendarElementColumns, CalendarHeadRows, CalendarHelper } from './calendar'\nimport ElementInfo from './element_info'\n\nfunction CalendarTable(props) {\n const { startDate, endDate, groupedAvailableElements, elementGroups,\n handleElementClick, rentedItems, rentList } = props\n\n const cal = new CalendarHelper(startDate, endDate)\n\n return (\n \n \n \n \n \n {Object.keys(groupedAvailableElements)\n .map(groupId => (\n \n \n \n \n {elementGroups[groupId]}\n \n | \n
\n {groupedAvailableElements[groupId].map(element => (\n handleElementClick(element)}>\n \n \n \n {element.name}\n \n \n \n \n \n | \n \n
\n ))}\n \n ))}\n \n
\n )\n}\n\nexport default CalendarTable\n","import React, { useEffect } from 'react'\nimport PropTypes from 'prop-types'\nimport SpinnerOverlay from 'components/shared/spinner_overlay'\nimport { Modal, ModalHeader, ModalBody } from 'reactstrap'\nimport CalendarTable from './calendar_table'\n\n\nconst { I18n } = window\n\nfunction RentList (props) {\n const {\n loadData, rentElement, unrentItem, rentList, groupedAvailableElements,\n elementGroups, rentElementRequestInProgress, rentedItems, initialRentedItems,\n errors, clearErrors,\n } = props\n const { startDate, endDate } = rentList\n\n useEffect(() => {\n const data = {\n elementGroups: rentList.elementGroups,\n groupedAvailableElements: rentList.groupedAvailableElements,\n rentedItems: initialRentedItems,\n rentListId: rentList.id,\n }\n loadData(data)\n }, [])\n\n const handleElementClick = (element) => {\n const rentItems = rentedItems[element.id]\n if (!rentItems) {\n rentElement(rentList.id, element.id)\n return\n }\n const rentItem = rentItems.find(i => i.rentList.id === rentList.id)\n if (rentItem) {\n unrentItem(rentList.id, rentItem.id)\n } else {\n rentElement(rentList.id, element.id)\n }\n }\n\n return (\n \n 0} toggle={clearErrors} size=\"lg\">\n \n {I18n.t('elements.rent_list.error_modal_header')}\n \n \n {errors.join('. ')}\n \n \n \n \n \n
\n )\n}\n\nRentList.propTypes = {\n loadData: PropTypes.func.isRequired,\n rentElement: PropTypes.func.isRequired,\n unrentItem: PropTypes.func.isRequired,\n rentList: PropTypes.shape({\n startDate: PropTypes.string.isRequired,\n endDate: PropTypes.string.isRequired,\n }).isRequired,\n rentElementRequestInProgress: PropTypes.bool.isRequired,\n rentedItems: PropTypes.object.isRequired,\n initialRentedItems: PropTypes.object.isRequired,\n groupedAvailableElements: PropTypes.object.isRequired,\n elementGroups: PropTypes.object.isRequired,\n}\n\nexport default RentList\n","import { connect } from 'react-redux'\nimport {\n loadData, rentElement, unrentItem, clearErrors,\n} from 'actions/rent_list_actions'\n\nfunction mapStateToProps (state, props) {\n return {\n elementGroups: state.getIn(['rentList', 'elementGroups'])?.toJS() || {},\n groupedAvailableElements: state.getIn(['rentList', 'groupedAvailableElements'])?.toJS() || {},\n rentElementRequestInProgress: state.getIn(['rentList', 'rentElementRequestInProgress']),\n rentedItems: state.getIn(['rentList', 'rentedItems'])?.toJS() || {},\n errors: state.getIn(['rentList', 'errors']).toJS() || [],\n }\n}\n\nfunction mapDispatchToProps (dispatch) {\n return {\n loadData: data => dispatch(loadData(data)),\n rentElement: (rentListId, elementId) => dispatch(rentElement(rentListId, elementId)),\n unrentItem: (rentListId, rentItemId) => dispatch(unrentItem(rentListId, rentItemId)),\n clearErrors: () => dispatch(clearErrors()),\n }\n}\n\nexport default connect(mapStateToProps, mapDispatchToProps)\n","import RentList from './rent_list'\nimport connect from './connect'\n\nexport default connect(RentList)\n","import React, { Fragment, useEffect } from 'react'\nimport PropTypes from 'prop-types'\nimport ReactHover, { Trigger, Hover } from 'react-hover'\nimport { CalendarHeadRows, CalendarElementColumns, CalendarHelper } from '../../rent_list_app/rent_list/calendar'\nimport ElementInfo from '../../rent_list_app/rent_list/element_info'\nimport CalendarTable from '../../rent_list_app/rent_list/calendar_table'\n\n\nconst { I18n } = window\n\nfunction RentListOverview (props) {\n const {\n initialData, loadData, startDate, endDate, groupedAvailableElements,\n elementGroups, rentedItems,\n } = props\n\n useEffect(() => {\n const data = {\n elementGroups: initialData.elementGroups,\n groupedAvailableElements: initialData.groupedAvailableElements,\n rentedItems: initialData.rentedItems,\n }\n loadData(data)\n }, [])\n\n const cal = new CalendarHelper(startDate, endDate)\n\n return (\n \n {}}\n rentedItems={rentedItems}\n rentList={null}\n />\n
\n )\n}\n\nRentListOverview.propTypes = {\n loadData: PropTypes.func.isRequired,\n startDate: PropTypes.string.isRequired,\n endDate: PropTypes.string.isRequired,\n initialData: PropTypes.object.isRequired,\n rentedItems: PropTypes.object.isRequired,\n groupedAvailableElements: PropTypes.object.isRequired,\n elementGroups: PropTypes.object.isRequired,\n}\n\nexport default RentListOverview\n","import { connect } from 'react-redux'\nimport { loadData } from 'actions/rent_list_actions'\n\nfunction mapStateToProps (state, props) {\n return {\n elementGroups: state.getIn(['rentList', 'elementGroups'])?.toJS() || {},\n groupedAvailableElements: state.getIn(['rentList', 'groupedAvailableElements'])?.toJS() || {},\n rentElementRequestInProgress: state.getIn(['rentList', 'rentElementRequestInProgress']),\n rentedItems: state.getIn(['rentList', 'rentedItems'])?.toJS() || {},\n errors: state.getIn(['rentList', 'errors']).toJS() || [],\n }\n}\n\nfunction mapDispatchToProps (dispatch) {\n return {\n loadData: data => dispatch(loadData(data)),\n }\n}\n\nexport default connect(mapStateToProps, mapDispatchToProps)\n","import RentListOverview from './rent_list_overview'\nimport connect from './connect'\n\nexport default connect(RentListOverview)\n","const I18n = window.I18n\n\n// TODO (atanych): the good idea to use Immutable.Record for models\nclass MyFile {\n constructor (attrs) {\n this.id = attrs.id\n this.status = attrs.status\n this.path = attrs.path\n this.name = attrs.name\n this.companyName = attrs.company_name\n }\n\n // TODO (atanych): in future we might create presenter level abstraction\n getIcon () {\n if (this.status === MyFile.FAILED) { return 'fa-exclamation' }\n if (this.status === MyFile.COMPLETED) { return 'fa-file-text-o' }\n if (this.status === MyFile.IN_PROGRESS) { return 'fa-spinner fa-fw fa-spin' }\n }\n\n getName () {\n if (this.status === MyFile.COMPLETED) { return this.name }\n return I18n.t(`my_files.index.file_name.${this.status}`)\n }\n\n isCompleted () {\n return this.status === MyFile.COMPLETED\n }\n\n isInProgress () {\n return this.status === MyFile.IN_PROGRESS\n }\n\n update (attrs) {\n _.each(attrs, (value, name) => { this[name] = value })\n }\n}\n\nMyFile.FAILED = 'failed'\nMyFile.COMPLETED = 'completed'\nMyFile.IN_PROGRESS = 'in_progress'\nexport default MyFile\n","import { Component } from 'react'\nimport PropTypes from 'prop-types'\nimport classNames from 'classnames'\n\nexport default class MyFile extends Component {\n static propTypes = {\n file: PropTypes.object,\n }\n\n getContainer (file) {\n if (file.isCompleted()) { return 'a' }\n return 'div'\n }\n\n getContainerAttrs (file) {\n if (!file.isCompleted()) { return {} }\n return { href: file.path }\n }\n\n clickDelete = () => {\n this.props.onDelete(this.props.file.id)\n }\n\n render () {\n const { file } = this.props\n const Container = this.getContainer(file)\n const statusStyle = `my-files-my-file-${file.status}`\n return (\n \n {file.companyName} | \n \n \n \n {file.getName()}\n \n | \n \n \n | \n
\n )\n }\n}\n","import MyFile from './my_file'\n\nexport default MyFile\n","import { Component } from 'react'\nimport PropTypes from 'prop-types'\nimport CableEmitter from 'emitters/cable_emitter'\nimport MyFileModel from 'models/my_file'\nimport MyFile from './my_file'\n\nexport default class MyFileList extends Component {\n static propTypes = {\n myFiles: PropTypes.array,\n }\n\n state = {\n myFiles: []\n }\n\n constructor (props) {\n super(props)\n // TODO (atanych): replace this structure with redux store\n this.state = this.buildState(props.myFiles)\n\n CableEmitter.addListener('MY_FILES.UPDATE', this.handleUpdate)\n CableEmitter.addListener('MY_FILES.CREATE', this.handleCreate)\n }\n\n handleUpdate = (newAttrs) => {\n const myFiles = this.state.myFiles.map((file) => {\n if (file.id !== newAttrs.id) { return file }\n file.update(newAttrs)\n return file\n })\n this.setState({ myFiles })\n }\n\n handleCreate = (newAttrs) => {\n const newFile = new MyFileModel(newAttrs)\n const myFiles = this.state.myFiles\n this.setState({ myFiles: [newFile].concat(myFiles) })\n }\n\n handleDelete = (id) => {\n ajax({url: `my_files/${id}`, method: 'delete'}).then((resp) => {\n this.setState(this.buildState(resp.data.my_files))\n })\n }\n\n buildState = (files) => {\n return { myFiles: files.map(file => new MyFileModel(file)) }\n }\n\n render () {\n const { myFiles } = this.state\n if (!myFiles.length) { return {I18n.t('my_files.index.empty_message')}
}\n\n return (\n \n \n \n {myFiles.map(file => )}\n \n
\n \n )\n }\n}\n","import List from './list'\n\nexport default List\n","import { Component } from 'react'\nimport PropTypes from 'prop-types'\nimport CableEmitter from 'emitters/cable_emitter'\nimport classNames from 'classnames'\nimport MyFileModel from 'models/my_file'\n\nexport default class HeaderLink extends Component {\n static propTypes = {\n inProgressIds: PropTypes.array,\n }\n\n state = {\n inProgressIds: []\n }\n\n constructor (props) {\n super(props)\n // TODO (atanych): replace this structure with redux store\n this.state = { inProgressIds: props.in_progress_ids }\n\n CableEmitter.addListener('MY_FILES.CREATE', this.handleCreate)\n CableEmitter.addListener('MY_FILES.UPDATE', this.handleUpdate)\n }\n\n handleCreate = (file) => {\n if (file.status !== MyFileModel.IN_PROGRESS) { return null }\n\n const { inProgressIds } = this.state\n this.setState({ inProgressIds: inProgressIds.concat(file.id) })\n }\n\n handleUpdate = (file) => {\n const { inProgressIds } = this.state\n if (file.status === MyFileModel.IN_PROGRESS && !inProgressIds.includes(file.id)) {\n this.setState({ inProgressIds: inProgressIds.concat(file.id) })\n } else {\n this.setState({ inProgressIds: inProgressIds.filter(id => id !== file.id) })\n }\n }\n\n getIcon () {\n const { inProgressIds } = this.state\n return inProgressIds.length ? 'fa-spinner fa-fw fa-spin' : 'fa-download'\n }\n render () {\n return (\n \n \n {I18n.t('my_files.index.title')}\n \n )\n }\n}\n","import { Component } from 'react'\nimport PropTypes from 'prop-types'\nimport PhotoswipeLink from 'components/shared/photoswipe/photoswipe_link'\n\nexport default class Link extends Component {\n static propTypes = {\n link: PropTypes.object.isRequired,\n photo: PropTypes.object.isRequired,\n }\n\n render () {\n const { photo, link } = this.props\n return (\n \n )\n }\n}\n","import { createSelector } from 'reselect'\n\nexport const getLinksByElement = createSelector(\n (links, elementId) => ({ links, elementId }),\n ({ links, elementId }) => links.filter(link => link.get('element_id') === elementId),\n)\n\n\nexport const getPhotoByLink = createSelector(\n (photos, link) => ({ photos, link }),\n ({ photos, link }) => photos.find(photo => photo.get('id') === link.get('attachment_id')),\n)\n","import { Component } from 'react'\nimport PropTypes from 'prop-types'\nimport Link from './link'\nimport * as selectors from 'reducers/project/selectors'\n\nexport default class List extends Component {\n static propTypes = {\n id: PropTypes.number.isRequired,\n type: PropTypes.string.isRequired,\n }\n\n onOpenModal = () => this.props.openImagesModal(this.props.id)\n\n renderPhotos() {\n const links = selectors.getLinksByElement(this.props.links, this.props.id)\n\n if (!links.size) {\n return (\n \n {I18n.t('element_photos.list.no')}\n
\n )\n }\n\n return (\n \n {links.map((link) => {\n const photo = selectors.getPhotoByLink(this.props.photos, link)\n if (!photo) return null\n\n return \n })}\n
\n )\n }\n\n render() {\n const { projectStatus, projectLoaded, type } = this.props\n if (!projectLoaded) {\n return null\n }\n if (projectStatus === 'approved') {\n return null\n }\n if (type !== 'Elements::LocationElement') {\n return null\n }\n\n return (\n \n \n
{I18n.t('element_photos.list.attached_images')}
\n {this.renderPhotos()}\n
\n \n \n )\n }\n}\n","import { connect } from 'react-redux'\nimport { openImagesModal } from 'actions/project_actions'\nimport * as selectors from 'reducers/project/selectors'\n\nfunction mapStateToProps (state) {\n return {\n projectLoaded: state.getIn(['project', 'transient', 'loaded']),\n projectStatus: state.getIn(['project', 'status']),\n links: state.getIn(['project', 'links']),\n photos: state.getIn(['project', 'photos']),\n }\n}\n\nfunction mapDispatchToProps (dispatch) {\n return {\n openImagesModal: elementId => dispatch(openImagesModal(elementId)),\n }\n}\n\nexport default connect(mapStateToProps, mapDispatchToProps)\n","import List from './list'\nimport connect from './connect'\n\nexport default connect(List)\n","import { Component } from 'react'\nimport store from 'store'\nimport { Provider } from 'react-redux'\nimport List from './list'\n\nexport default class App extends Component {\n render () {\n return (\n \n
\n \n )\n }\n}\n","import React, { useState } from 'react'\nimport PropTypes from 'prop-types'\nimport PhotoswipeImageLink from 'components/shared/photoswipe/photoswipe_image_link'\nimport SmModalWindow from 'components/shared/modal_window'\nimport Uploader from 'components/uploader_v2_app/uploader'\n\nconst { I18n } = window\n\nexport default function Photos (props) {\n const { photoFiles, uploaderOptions } = props\n const [dialogOpened, setDialogOpened] = useState(false)\n const buttonTitle = I18n.t('elements.edit_photos')\n const modalContent = (\n \n \n
\n )\n\n return (\n \n {photoFiles.map(photo => (\n
\n ))}\n
\n
{ setDialogOpened(false) }}\n content={modalContent}\n windowID={`element-photos-uploader-${1}`}\n />\n \n )\n}\n","import { connect } from 'react-redux'\n\nfunction mapStateToProps (state, props) {\n return {\n photoFiles: state.getIn(['uploader', props.uploaderOptions.buttonId, 'files'])?.toJS() || {}\n }\n}\n\nfunction mapDispatchToProps (dispatch) {\n return {}\n}\n\nexport default connect(mapStateToProps, mapDispatchToProps)\n","import Photos from './photos'\nimport connect from './connect'\n\nexport default connect(Photos)\n","import { Component } from 'react'\nimport PropTypes from 'prop-types'\nimport classNames from 'classnames'\n\nexport default class Photo extends Component {\n static propTypes = {\n photo: PropTypes.object,\n }\n\n onClick = (e) => {\n const { onClick, selected, photo } = this.props\n\n onClick(photo.get('id'), !selected)\n }\n\n render () {\n const { photo, selected } = this.props\n return (\n \n )\n }\n}\n","import { Component } from 'react'\nimport PropTypes from 'prop-types'\nimport SmModalWindow from 'components/shared/modal_window'\nimport Photo from './photo'\n\nconst MODAL_WINDOW_ID = 'attach-photo-to-element'\nexport default class AttachPhotoToElementModal extends Component {\n \n static propTypes = {\n photos: PropTypes.object.isRequired,\n links: PropTypes.object.isRequired,\n projectId: PropTypes.number,\n activeElement: PropTypes.number,\n reservedLinks: PropTypes.any,\n togglePhoto: PropTypes.func.isRequired,\n resetLinks: PropTypes.func.isRequired,\n saveLinks: PropTypes.func.isRequired,\n }\n\n componentDidMount () {\n const { resetLinks } = this.props\n\n $(`#${MODAL_WINDOW_ID}`).on('hidden.bs.modal', () => {\n if (this.props.reservedLinks) { resetLinks() }\n })\n }\n\n isSelectedPhoto (photoId) {\n const { links, activeElement } = this.props\n return links.find(link => link.get('attachment_id') === photoId && link.get('element_id') === activeElement)\n }\n\n onSubmit = () => {\n const { saveLinks, projectId, links, activeElement } = this.props\n const photoIds = links.filter(link => (\n link.get('element_id') === activeElement\n )).map(link => link.get('attachment_id'))\n saveLinks(projectId, activeElement, photoIds)\n }\n\n renderContent () {\n const { photos, togglePhoto } = this.props\n if (!photos.size) { return {I18n.t('attach_photo_to_element_modal.no_photos')}
}\n\n return photos.map(photo => )\n }\n\n renderButtons () {\n return (\n \n \n \n
\n )\n }\n\n render () {\n return (\n \n )\n }\n}\n","import { connect } from 'react-redux'\nimport { togglePhoto, resetLinks, saveLinks } from 'actions/project_actions'\n\nfunction mapStateToProps (state) {\n return {\n projectId: state.getIn(['project', 'id']),\n links: state.getIn(['project', 'links']),\n photos: state.getIn(['project', 'photos']),\n reservedLinks: state.getIn(['project', 'transient', 'reservedLinks']),\n activeElement: state.getIn(['element', 'transient', 'activeElement']),\n }\n}\n\nfunction mapDispatchToProps (dispatch) {\n return {\n togglePhoto: (photoId, selected) => dispatch(togglePhoto(photoId, selected)),\n resetLinks: () => dispatch(resetLinks()),\n saveLinks: (projectId, elementId, photoIds) => dispatch(saveLinks(projectId, elementId, photoIds)),\n }\n}\n\nexport default connect(mapStateToProps, mapDispatchToProps)\n","import AttachPhotoToElementModal from './attach_photo_to_element_modal'\nimport connect from './connect'\n\nexport default connect(AttachPhotoToElementModal)\n","import { Component } from 'react'\nimport store from 'store'\nimport { Provider } from 'react-redux'\nimport { loadProject } from 'actions/project_actions'\nimport AttachPhotoToElementModal from './attach_photo_to_element_modal'\n\nexport default class Project extends Component {\n componentDidMount () {\n store.dispatch(loadProject(this.props))\n }\n\n render () {\n return (\n \n \n \n )\n }\n}\n","import { Component } from 'react'\n\nlet basket = null\n\nexport class Basket extends Component {\n\n constructor (props) {\n super(props)\n basket = this\n this.state = {\n count: props.count,\n containerClass: this.containerClassName(props.count)\n }\n }\n\n handleCountUpdate = count => this.setState({ count, containerClass: this.containerClassName(count) })\n\n containerClassName = count => count === 0 ? 'hidden' : 'basket-extra-amounts'\n\n handleCancelClick = e =>\n $.ajax({\n url: this.props.cancelUrl,\n type: 'delete',\n dataType: 'json',\n success: resp => window.location = resp.location,\n })\n\n handlePreviewClick = (e) => window.location = this.props.indexUrl\n\n render () {\n return (\n \n
{`${this.props.title}: ${this.state.count}`}
\n \n \n \n )\n }\n}\n\nexport class Control extends Component {\n constructor (props) {\n super(props)\n this.state = {\n value: props.value,\n btnClassName: props.value === 0 ? 'hidden' : 'reset-control',\n method: props.method,\n url: props.url,\n }\n }\n handleValueChange = (e) => {\n const value = e.target.value\n if (value === '') { return this.setState({ value }) }\n this.sendData(value)\n }\n\n handleReset = e => this.sendData(0)\n\n sendData = (value) => {\n $.ajax({\n url: this.state.url,\n type: this.state.method,\n data: { value, element_id: this.props.element_id },\n dataType: 'json',\n success: (resp) => {\n let newStateValues = {}\n newStateValues.value = parseInt(resp.value)\n newStateValues.btnClassName = value === 0 ? 'hidden' : 'reset-control'\n if (resp.url) { newStateValues.url = resp.url }\n if (resp.method) { newStateValues.method = resp.method }\n this.setState(newStateValues)\n // It is ugly hack.\n // We might replace with store, but I'm sorry, dont have time to replace within current task\n basket.handleCountUpdate(resp.count)\n }\n })\n }\n\n render () {\n return (\n \n \n \n
\n )\n }\n}\n","import { EventEmitter } from 'fbemitter'\n\nclass FormsDataStore extends EventEmitter {\n\n constructor () {\n super()\n this.forms = { 'by-type': { } }\n }\n\n init (mainKey, data) {\n this.forms[mainKey] = $.extend(true, {}, data)\n }\n\n assign(mainKey, fieldKey, data) {\n this.forms[mainKey][fieldKey] = data\n }\n\n get (mainKey, fieldKey) {\n return this.forms[mainKey][fieldKey]\n }\n\n formDataFor(keypath) {\n let mainKey, fieldKey\n [mainKey, fieldKey] = keypath.split('.')\n if (mainKey && fieldKey) return this.forms[mainKey][fieldKey]\n else return formData[fieldKey]\n }\n\n trigger (eventName) {\n this.emit(eventName)\n }\n}\n\nexport default new FormsDataStore\n","import * as React from 'react'\nimport { Component } from 'react'\nimport { format, subYears } from 'date-fns'\nimport store from 'stores/forms_data_store'\nimport Select from 'components/shared/base_react_select'\nimport DatePicker from 'react-datepicker'\n\nclass ProjectExpensesFiltersForm extends Component {\n\n constructor (props) {\n super(props)\n\n this.defaultValues = {\n startAt: null,\n endAt: null,\n selectedLocations: [],\n selectedCompanies: [],\n selectedVendors: [],\n formRef: this\n }\n\n store.init('by-type', this.defaultValues)\n\n this.listener = store.addListener('UpdateProjectExpensesFormView', () => this.forceUpdate())\n }\n\n filtersData () {\n return {\n start_at: store.forms['by-type'].startAt ? format(store.forms['by-type'].startAt, 'yyyy-MM-dd') : null,\n end_at: store.forms['by-type'].endAt ? format(store.forms['by-type'].endAt, 'yyyy-MM-dd') : null,\n location_ids: _.map(store.forms['by-type'].selectedLocations, (loc) => loc.id),\n company_ids: _.map(store.forms['by-type'].selectedCompanies, (comp) => comp.id),\n vendor_ids: _.map(store.forms['by-type'].selectedVendors, (vend) => vend.id)\n }\n }\n\n componentWillUnmount () {\n this.listener.remove()\n }\n\n onChangeSelect (val, field) {\n store.assign('by-type', field, val)\n this.forceUpdate()\n }\n\n onClickFilter (e) {\n store.trigger('FilterClicked')\n }\n\n onClickReset (e) {\n store.init('by-type', this.defaultValues)\n this.forceUpdate()\n store.trigger('FilterClicked')\n }\n\n prepareDatePickerProps (params) {\n return {\n dateFormat: 'yyyy-MM-dd',\n showMonthDropdown: true,\n showYearDropdown: true,\n minDate: subYears(new Date(), 10),\n maxDate: new Date(),\n className: 'form-control-sm',\n name: params.key,\n selected: store.get('by-type', params.key),\n placeholderText: this.props.formData.placeholders[params.key],\n onChange: (date) => {\n store.assign('by-type', params.key, date)\n this.forceUpdate()\n },\n }\n }\n\n render () {\n return (\n \n )\n }\n}\n\nexport default ProjectExpensesFiltersForm\n","import ProjectExpensesFiltersForm from './additional_reports/by_type/project_expenses'\nimport { Component } from 'react'\nconst FORMS = {\n 'project_expenses': ProjectExpensesFiltersForm\n}\n\nexport default class FormByType extends Component {\n\n render () {\n const CurrentForm = FORMS[this.props.type]\n return (\n \n \n
\n )\n }\n}\n","import FormByType from './by_type'\n\nexport default {\n FormByType\n}\n","import ImageList from './views/thumbnails_collection/image_list'\n\nexport default {\n ImageList\n}\n","import React from 'react'\nimport { connect } from 'react-redux'\nimport AlbumName from './views/albums/name'\nimport Photo from './views/albums/photo'\n\nclass FavoriteAlbum extends React.Component {\n\n render () {\n const { photos, title } = this.props\n\n if (photos.length == 0) { return null }\n\n return (\n \n
\n
\n { photos.map((photo) =>
\n )}\n
\n
\n )\n }\n}\n\nexport default connect((state, props) => {\n const favoritePhotos = props.uploaderIds.reduce((result, uploaderId) => {\n result = result.concat(\n state.getIn(['uploader', uploaderId, 'files']).filter(f => f.getIn(['favorite', 'enabled'])).toJS()\n )\n return result\n }, [])\n\n return ({\n title: props.title,\n photos: favoritePhotos\n })\n}, {})(FavoriteAlbum)\n","import React from 'react'\nimport store from 'store'\nimport { Provider } from 'react-redux'\nimport { initAlbums } from 'actions/uploader_actions'\nimport Uploader from './uploader'\nimport FavoriteAlbum from './favorite_album'\n\nconst Uploaders = ({ ids }) => {\n return (\n <>\n { ids.map((buttonId, index) => (\n \n ))}\n >\n )\n}\n\nconst AlbumsApp = (props) => {\n const { uploaderIds, uploaders, favAlbumTitle } = props\n\n store.dispatch(initAlbums(uploaders))\n\n return (\n \n \n \n \n )\n}\n\nexport default AlbumsApp\n","import { connect } from 'react-redux'\nimport {\n selectItem,\n selectAll,\n removeItem,\n removeAll,\n saveSelected,\n filterBy,\n filterByTags,\n nextStep,\n stepBack,\n} from '../../../actions/campaigns/locations_selector_actions'\nimport { selectByTags } from '../../../actions/campaigns/common_actions'\n\nexport default connect(\n (state: any) => ({\n id: state.getIn(['campaigns', 'id']),\n approvedLocationIDs: state.getIn(['campaigns', 'approvedLocationIDs']),\n campaignLocationsNeedApproval: state.getIn(['campaigns', 'campaignLocationsNeedApproval']),\n exportLocationApprovalsUrl: state.getIn(['campaigns', 'exportLocationApprovalsUrl']),\n locations: state.getIn(['campaigns', 'locations']),\n tags: state.getIn(['campaigns', 'tags']),\n regions: state.getIn(['campaigns', 'regions']),\n isReady: state.getIn(['campaigns', 'isReady']),\n }),\n {\n selectItem,\n removeItem,\n selectAll,\n removeAll,\n saveSelected,\n filterBy,\n filterByTags,\n selectByTags,\n nextStep,\n stepBack,\n }\n)\n","import classNames from 'classnames'\n\nexport const COLOR_BY_TYPE = {\n 'Tags::Manual' : 'green',\n 'Tags::Auto' : 'blue',\n 'Tags::BrandTag' : 'yellow',\n 'Tags::Approved::Yes' : 'green',\n 'Tags::Approved::No' : 'red'\n}\n\nconst dot = (color = '#ccc') => ({\n alignItems: 'center',\n display: 'inline-block',\n backgroundColor: color,\n borderRadius: 10,\n content: '\" \"',\n marginRight: 8,\n marginLeft: 4,\n height: 10,\n width: 10,\n zIndex: 99\n});\n\nexport default function TagBadge(props) {\n const color = COLOR_BY_TYPE[props.type] || 'white'\n\n return (\n \n \n {props.name}\n \n )\n}\n","import React from 'react'\nimport TagBadge from 'components/shared/tags/badge'\n\nconst I18n = window.I18n\n\nfunction Direct({ number, name, regionName, action, address, showRegions }) {\n address = address ? address.toJS() : {}\n\n let addressString = ''\n if (Object.keys(address).length > 0) {\n let addressParts = [\n address.line1,\n address.line2,\n address.zip,\n address.city,\n address.country,\n ]\n\n addressParts = addressParts.filter((part) => part !== null && part !== undefined)\n\n if (addressParts.length > 0) {\n addressString = addressParts.join(', ')\n }\n }\n return (\n \n {number} | \n {name} | \n {showRegions && {regionName} | }\n {addressString} | \n \n \n {I18n.t('application.actions.select')}\n \n \n \n | \n
\n )\n}\n\nfunction Reverse({ id, number, name, action, tags, approvedLocationIDs, campaignLocationsNeedApproval }) {\n tags = tags ? tags.toJS() : []\n\n return (\n \n \n {tags.length == 0 && (\n \n \n \n {I18n.t('application.actions.remove')}\n \n )}\n | \n {number} | \n \n {name}\n {tags.map((tag) => (\n \n ))}\n | \n {campaignLocationsNeedApproval && (\n \n {approvedLocationIDs.includes(id) ? (\n \n ) : (\n \n )}\n | \n )}\n
\n )\n}\n\nconst DIRECTIONS = { direct: Direct, reverse: Reverse }\n\nexport default class RowItem extends React.Component {\n handleAction = () => {\n this.props.action(this.props.item.get('id'))\n }\n\n render() {\n const { item, direction, showRegions, approvedLocationIDs, campaignLocationsNeedApproval } = this.props\n const RowItemView = DIRECTIONS[direction] || Direct\n\n return (\n \n )\n }\n}\n","import React from 'react'\nimport RowItem from './row_item'\n\nconst I18n = window.I18n\n\nexport default class SourceBox extends React.Component {\n\n render () {\n const { items, selectItem, selectAll, showRegions } = this.props\n return (\n \n \n \n {I18n.t('activerecord.attributes.elements/base_element.location_number')} | \n {I18n.t('activerecord.models.location.other')} ({items.size}) | \n {showRegions && {I18n.t('activerecord.models.regions/region.one')} | }\n {I18n.t('activerecord.attributes.elements/base_element.location_address')} | \n \n \n {I18n.t('application.actions.select_all')}\n \n \n \n | \n
\n \n \n {items.map((item) => )}\n \n
\n )\n }\n}\n","import React from 'react'\nimport RowItem from './row_item'\n\nconst I18n = window.I18n\n\nexport default class TargetBox extends React.Component {\n render() {\n const { items, removeItem, removeAll, approvedLocationIDs, campaignLocationsNeedApproval } = this.props\n return (\n \n \n \n \n \n \n \n {I18n.t('application.actions.remove_all')}\n \n | \n {I18n.t('activerecord.attributes.elements/base_element.location_number')} | \n {I18n.t('activerecord.models.location.other') } ({items.size}) | \n {campaignLocationsNeedApproval && (\n {I18n.t('activerecord.attributes.elements/base_element.approved.approved')} | \n )}\n
\n \n \n {items.map((item) => (\n \n ))}\n \n
\n )\n }\n}\n","import * as React from 'react'\nimport { Map, List } from 'immutable'\nimport { SourceBox, TargetBox } from './boxes'\n\nconst { I18n } = window as any\n\nconst splitLocationsOnTwoTypes = (locations) => {\n let sourceList = List()\n let targetList = List()\n\n locations.forEach((location) => {\n if (location.get('selected')) {\n targetList = targetList.push(location)\n } else if (location.get('visible')) {\n sourceList = sourceList.push(location)\n }\n })\n\n return Map({ source: sourceList, target: targetList })\n}\n\ntype LocationsContentProps = {\n locations: any\n actions: any\n tags: any\n regions: any\n ByTagsSelector: any\n approvedLocationIDs: any\n campaignLocationsNeedApproval: any\n exportLocationApprovalsUrl: string\n}\n\nexport default function LocationsContent(props: LocationsContentProps) {\n const { actions, locations, tags, regions, ByTagsSelector, approvedLocationIDs, campaignLocationsNeedApproval, exportLocationApprovalsUrl } =\n props\n const splitLocations = splitLocationsOnTwoTypes(locations)\n\n const filterBy = (e) => {\n actions.filterBy(e.target.value)\n }\n\n const filterByTags = (filterRegions) => {\n actions.filterByTags(filterRegions)\n }\n\n return (\n \n
\n
\n {campaignLocationsNeedApproval && (\n
\n )}\n
\n \n
\n
\n
\n )\n}\n","import React from 'react'\n\nconst I18n = window.I18n\n\nexport default class SaveButton extends React.Component {\n\n render () {\n return (\n \n )\n }\n}\n","import React from 'react'\n\nconst I18n = window.I18n\n\nexport default class SaveAndGoButton extends React.Component {\n render() {\n return (\n \n )\n }\n}\n","import React from 'react'\n\nconst I18n = window.I18n\n\nexport default class NextButton extends React.Component {\n\n render () {\n return (\n \n )\n }\n}\n","import React from 'react'\n\nconst I18n = window.I18n\n\nexport default class BackButton extends React.Component {\n render() {\n return (\n \n )\n }\n}\n","import React from 'react'\nimport Select from 'components/shared/base_react_select'\nimport { COLOR_BY_TYPE } from 'components/shared/tags/badge'\n\nconst I18n = window.I18n\n\nconst dot = (color = '#ccc') => ({\n alignItems: 'center',\n display: 'flex',\n ':before': {\n backgroundColor: color,\n borderRadius: 10,\n content: '\" \"',\n display: 'block',\n marginRight: 4,\n marginLeft: 8,\n height: 10,\n width: 10,\n },\n})\n\nconst Styles = {\n option: (styles, { data }) => ({...styles, ...dot(COLOR_BY_TYPE[data.type])}),\n multiValue: (styles, { data }) => ({ ...styles, ...dot(COLOR_BY_TYPE[data.type]) }),\n}\n\nconst Placeholder = I18n.t('application.actions.select_by_what', {\n what: I18n.t('activerecord.models.tag.other')\n})\n\nexport default function ({ items, selectTag }) {\n const value = items.filter(tag => tag.selected)\n \n return (\n \n \n
\n )\n}\n","import * as React from 'react'\nimport connect from './connect'\nimport { LocationsContent } from './contents'\nimport SaveButton from '../common/save_button'\nimport SaveAndGoButton from '../common/save_and_go_button'\nimport NextButton from '../common/next_button'\nimport BackButton from '../common/back_button'\nimport ByTagsSelector from '../by_tags_selector'\n\ntype LocationsSelectorProps = {\n locations: any\n tags: any\n regions: any\n isReady: boolean\n approvedLocationIDs: any\n campaignLocationsNeedApproval: any\n exportLocationApprovalsUrl: string\n id: number\n saveSelected: any\n removeItem: any\n removeAll: any\n selectItem: any\n selectAll: any\n selectByTags: any\n filterBy: any\n filterByTags: any\n stepBack: any\n}\n\nfunction LocationsSelector(props: LocationsSelectorProps) {\n const {\n locations,\n tags,\n regions,\n isReady,\n approvedLocationIDs,\n campaignLocationsNeedApproval,\n exportLocationApprovalsUrl,\n id,\n saveSelected,\n removeItem,\n removeAll,\n selectItem,\n selectAll,\n selectByTags,\n filterBy,\n filterByTags,\n stepBack,\n } = props\n\n const handleSave = (e, stepAction = null, next = null) => {\n const locationIDs = locations.reduce((result, iLocation) => {\n if (iLocation.get('selected')) {\n result.push(iLocation.get('id'))\n }\n return result\n }, [])\n const tagIDs = tags.reduce((result, iTag) => {\n if (iTag.get('selected')) {\n result.push(iTag.get('value'))\n }\n return result\n }, [])\n if (locationIDs.length || tagIDs.length) {\n saveSelected(id, locationIDs, tagIDs, stepAction, next)\n }\n }\n\n const handleSaveAndGoToOverview = (e) => {\n handleSave(e, 'save_and_go_to_overview')\n }\n\n const handleNext = (e) => {\n handleSave(e, null, true)\n }\n\n const handleBack = () => {\n stepBack(id)\n }\n\n const actions = {\n removeItem,\n removeAll,\n selectItem,\n selectAll,\n selectByTags,\n filterBy,\n filterByTags,\n }\n\n return (\n \n
\n
\n
\n \n
\n
\n \n
\n {isReady && (\n
\n \n
\n )}\n
\n \n
\n
\n
\n )\n}\n\nexport default connect(LocationsSelector)\n","import * as React from 'react'\nimport { useEffect } from 'react'\nimport { Provider } from 'react-redux'\nimport store from '../../store'\nimport LocationsSelector from './locations_selector'\nimport { initBoxes } from '../../actions/campaigns/locations_selector_actions'\nimport { selectByTags } from '../../actions/campaigns/common_actions'\n\ntype LocationsSelectorAppProps = {\n id: number\n approvedLocationIDs: number[]\n campaignLocationsNeedApproval: boolean\n exportLocationApprovalsUrl: string\n step: number\n locations: any\n tags: any\n regions: any\n isReady: boolean\n selectedTags: any\n}\n\nfunction LocationsSelectorApp(props: LocationsSelectorAppProps) {\n const {\n id,\n approvedLocationIDs,\n campaignLocationsNeedApproval,\n exportLocationApprovalsUrl,\n locations,\n tags,\n selectedTags,\n isReady,\n step,\n regions,\n } = props\n\n useEffect(() => {\n store.dispatch(\n initBoxes({\n id,\n approvedLocationIDs,\n campaignLocationsNeedApproval,\n exportLocationApprovalsUrl,\n step,\n locations,\n tags,\n regions,\n isReady,\n })\n )\n\n if (selectedTags.length) {\n store.dispatch(selectByTags(selectedTags, 'locations', true))\n }\n }, [])\n\n return (\n \n \n \n )\n}\n\nexport default LocationsSelectorApp\n","import { connect } from 'react-redux'\nimport { removeItem, selectItems, saveSelected,\n updateItem, makeCopy, assignActiveItem,\n updateItemImageList, mergeItemImages, removeItemImage, addNewImageToItem, updateItemImage,\n createElement, addAllElements, types, selectTags, nextStep, stepBack\n} from 'actions/campaigns/elements_selector_actions'\nimport { selectByTags } from 'actions/campaigns/common_actions'\nimport { createLoadingSelector } from 'libs/requests_status_handlers'\n\nconst loadingSelector = createLoadingSelector([types.CREATE_ELEMENT])\n\nexport default connect((state) => ({\n id: state.getIn(['campaigns', 'id']),\n isReady: state.getIn(['campaigns', 'isReady']),\n isFetching: loadingSelector(state),\n elements: state.getIn(['campaigns', 'elements']),\n tags: state.getIn(['campaigns', 'tags']),\n elementFormData: state.getIn(['campaigns', 'elementFormData']),\n activeItem: state.getIn(['campaigns', 'activeItem']),\n imageDeleteCandidate: state.getIn(['campaigns', 'imageDeleteCandidate'])\n}), { selectItems, removeItem, updateItem, \n updateItemImageList, mergeItemImages, addNewImageToItem, updateItemImage, removeItemImage,\n saveSelected, selectTags, makeCopy, assignActiveItem, createElement, addAllElements,\n selectByTags, nextStep, stepBack\n})\n","import Select from 'components/shared/base_react_select'\n\nexport default function ({itemInterface, values, onChange, placeholder, options}) {\n return (\n \n )\n}\n","import Interfaces from 'components/shared/selector_interfaces'\n// import { css } from '@emotion/react'\n// import styles from './styles'\n\n// const CustomOption = (props) => {\n// const { children, className, cx, getStyles, isDisabled, isFocused, isSelected, innerRef, innerProps, data } = props\n// console.log(css(getStyles('option', props)), getStyles('option', props))\n// const cssClasses = cx(css(getStyles('option', props)), {\n// 'option': true,\n// 'option--is-disabled': isDisabled,\n// 'option--is-focused': isFocused,\n// 'option--is-selected': isSelected,\n// }, className)\n//\n// return (!isDisabled ? (\n// (\n// \n//
\n// {data.value} {data.prefix}\n// {data.name}\n// {data.otherLabel}\n//
\n//
\n// )\n// ) : null)\n// }\n\nclass ElementItemInterface extends Interfaces.ElementItemInterface {\n\n extractBaseItemAttrs (item) {\n return Object.assign(super.extractBaseItemAttrs(item), {\n campaignElementID: item.campaignElementID\n })\n }\n\n extendOptionItem (item) {\n return Object.assign(super.extendOptionItem(item), { prefix: item.prefix })\n }\n\n // customOptionComponent = () => CustomOption\n}\n\nexport default ElementItemInterface\n","import React, { Fragment } from 'react'\nimport ElementsSelector from './selector'\nimport ElementItemInterface from './selector/element_item_interface'\nimport CompositElementID from 'models/campaigns/composit_element_id'\n\nconst itemInterface = new ElementItemInterface()\n\nconst I18n = window.I18n\n\nconst Placeholder = I18n.t('application.actions.select_what', {\n what: I18n.t('activerecord.models.elements/common_element.other').toLowerCase()\n}) + '...'\n\nclass Selector extends React.Component {\n\n constructor (props) {\n super(props)\n this.state = { values: [] }\n }\n\n onChange = (opt) => {\n this.props.selectItems([\n new CompositElementID(opt.campaignElementID, opt.value, opt.type, null)\n ])\n }\n\n render () {\n const options = this.props.items\n return (\n \n \n )\n }\n}\n\nexport default Selector\n","import React, { Fragment } from 'react'\nimport ElementsSelector from './selector'\nimport ElementItemInterface from './selector/element_item_interface'\nimport CompositElementID from 'models/campaigns/composit_element_id'\n\nconst itemInterface = new ElementItemInterface()\n\nconst I18n = window.I18n\n\nconst Placeholder = I18n.t('application.actions.select_what', {\n what: I18n.t('activerecord.models.elements/location_element.other').toLowerCase()\n}) + '...'\n\nclass Selector extends React.Component {\n\n constructor (props) {\n super(props)\n this.state = { values: [] }\n }\n\n onChange = (opt) => {\n this.props.selectItems([\n new CompositElementID(opt.campaignElementID, opt.value, opt.type, null)\n ])\n }\n\n render () {\n const options = this.props.items\n return (\n \n \n \n )\n }\n}\n\nexport default Selector\n","import TagBadge from 'components/shared/tags/badge'\n\nconst ItemTags = ({ tags }) => {\n if (tags.length == 0) { return null }\n\n return (\n \n {tags.map(tag => )}\n
\n )\n}\n\nexport default ItemTags\n","const ItemName = ({ isCopy, onChange, name }) => {\n if (isCopy) {\n return \n }\n else return name\n}\n\nexport default ItemName\n","const LocationName = ({ name }) => {\n if (!name) { return null }\n\n return (\n [{name}]\n )\n}\n\nexport default LocationName\n","import React from 'react'\n\nconst { I18n } = window\n\nexport const RemoveLnk = ({ onClick }) => {\n return (\n {\n e.preventDefault()\n onClick(e)\n }}\n >\n {I18n.t('application.actions.remove')}\n \n )\n}\n\nexport const CloneLnk = ({ onClick }) => {\n return (\n {\n e.preventDefault()\n onClick(e)\n }}\n >\n {I18n.t('application.actions.clone')}\n \n )\n}\n","export const COLLAPSE_EXPAND_STATES = { collapsed: 'collapsed', expanded: 'expanded' }\n\nconst Expand = ({ changeAction, ...props }) => {\n\n const onClick = () => changeAction(COLLAPSE_EXPAND_STATES.expanded, props)\n\n return (\n \n )\n}\n\nconst Collapse = ({ changeAction, ...props }) => {\n\n const onClick = () => changeAction(COLLAPSE_EXPAND_STATES.collapsed, props)\n\n return (\n \n )\n}\n\nexport const isExpanded = (currentState) => (currentState == COLLAPSE_EXPAND_STATES.expanded)\n\nexport default ({ currentState, changeAction, ...props }) => {\n if (!currentState) { return null }\n\n if (currentState == COLLAPSE_EXPAND_STATES.collapsed) {\n return \n }\n\n return \n}\n","import * as React from 'react'\nimport UploaderV2 from '../../../../uploader_v2_app/uploader'\nimport LnkSplitter from '../../../../shared/lnk_splitter'\nimport ItemTags from './item_tags'\nimport ItemName from './item_name'\nimport LocationName from './location_name'\nimport { CloneLnk, RemoveLnk } from './action_links'\nimport CollapseExpandControl from '../../../../shared/collapse_expand'\nimport TagBadge from '../../../../shared/tags/badge'\n\ntype ElementRowProps = {\n item: any\n onChange: any\n clickSelect: any\n clickClone: any\n clickRemove: any\n uploaderProps: any\n expandStateAction: any\n}\n\nexport default function ElementRow(props: ElementRowProps) {\n const { item, onChange, clickSelect, clickClone, clickRemove, uploaderProps, expandStateAction } = props\n const { locationName, isCopy, name, dimensions, note, minAmount, maxAmount, tags, matchedTags, tagIDs } = item\n const { I18n } = window as any\n\n const actions = []\n if (!item.isLocation) {\n actions.push()\n }\n if (matchedTags == 0) {\n if (actions.length > 0) {\n actions.push()\n }\n actions.push()\n }\n\n return (\n \n \n {tagIDs.length > 0 && }\n \n \n \n | \n \n \n | \n \n \n | \n {dimensions} | \n \n \n | \n \n \n | \n \n {I18n.t('application.actions.select')}\n \n {tags.map((tag) => (\n \n ))}\n \n | \n {actions} | \n
\n )\n}\n","import { connect } from 'react-redux'\nimport { setTagAmount } from 'actions/campaigns/elements_selector_actions'\n// import { createLoadingSelector } from 'libs/requests_status_handlers'\n\n// const loadingSelector = createLoadingSelector([types.CREATE_ELEMENT])\n\nexport default connect((state) => ({\n allTags: state.getIn(['campaigns', 'tags']).toJS(),\n}), { setTagAmount })\n","const I18n = window.I18n\nconst $ = window.$\n\nconst inlineErrorClass = 'inline-error'\nconst inlineErrorSelector = `.${inlineErrorClass}`\nconst valueCellSelector = '.amount-cell'\n\nexport const buildMessageFor = (min, max) => {\n return I18n.t('activerecord.errors.models.elements/base_element.attributes.amount.exceed_min_and_max_limit')\n .replace('%{min}', min).replace('%{max}', max)\n}\n\nexport const valueBasedOnMinMaxIsValid = (val, min, max) => {\n if (val && min && val < min) { return false }\n if (val && max && val > max) { return false }\n return true\n}\n\nexport const inlineError = (msg) => {\n return `${msg}
`\n}\n\nexport const showErrorMsg = ($inp, min, max) => {\n const $parent = $inp.parent(valueCellSelector)\n if ($(inlineErrorSelector, $parent).size() == 0) {\n const msg = buildMessageFor(min, max)\n $(inlineError(msg)).insertAfter($inp)\n }\n}\n\nexport const hideErrorMsg = ($inp) => {\n const $parent = $inp.parent(valueCellSelector)\n $(inlineErrorSelector, $parent).remove()\n}\n\nexport default {\n buildMessageFor, valueBasedOnMinMaxIsValid, inlineErrorClass\n}\n","import MinMaxErrorsHelper from 'views/campaigns/helpers/min_max_validator'\n\nconst ErrorMsg = ({ msg }) => {\n if (!msg) { return null }\n\n return (\n {msg}
\n )\n}\n\nconst TagWithAmountInput = ({ tag, amount, changeTagAmount, min, max }) => {\n const { name, type, value } = tag\n\n const onChange = (e) => changeTagAmount(tag.value, e.target.value)\n\n const htmlTagId = `tag_amount_${value}`\n\n let errorMsg = ''\n if (!MinMaxErrorsHelper.valueBasedOnMinMaxIsValid(amount, min, max)) {\n errorMsg = MinMaxErrorsHelper.buildMessageFor(min, max)\n }\n\n return (\n \n \n \n \n
\n )\n}\n\nexport default TagWithAmountInput\n","import connect from './connect'\nimport TagWithAmountInput from './tag_with_amount_input'\n\n\nconst TagsWithAmountsRow = ({ allTags, item, visible, setTagAmount }) => {\n if (!visible) { return null }\n\n const tags = allTags.filter(tag => item.tagIDs.includes(tag.value) )\n\n const changeTagAmount = (tagId, val) => setTagAmount(item, tagId, val)\n\n return (\n \n \n \n { tags.map(tag =>\n \n )}\n \n | \n
\n )\n}\n\nexport default connect(TagsWithAmountsRow)\n","import { useState, useMemo } from 'react'\nimport { isEmpty } from 'lodash'\nimport ElementRow from './element_row'\nimport TagsWithAmountsRow from './tags_with_amounts_row'\nimport { COLLAPSE_EXPAND_STATES, isExpanded } from 'components/shared/collapse_expand'\n\nconst I18n = window.I18n\n\nconst RowItem = ({item, makeCopy, removeItem, updateItem, clickTagsSelectorControl, imageActions}) => {\n\n const { tagsAmounts, images } = item\n\n const [collapseExpandState, changeCollapseExpandState] = useState(\n (isEmpty(tagsAmounts) ? COLLAPSE_EXPAND_STATES.collapsed : COLLAPSE_EXPAND_STATES.expanded)\n )\n\n const toggleTagsRowVisibility = (newCollapseExpandState) => changeCollapseExpandState(newCollapseExpandState)\n\n const clickClone = () => makeCopy(item)\n\n const clickRemove = () => removeItem(item)\n\n const onChange = (e) => updateItem(item, e.target.name, e.target.value)\n\n const imageListUpdated = (store) => updateItemImageList(item, Array.from(store.files))\n\n const removeImage = (file) => removeItemImage(item, file.id)\n\n const clickSelect = () => clickTagsSelectorControl(item)\n\n\n //const images = Array.from(this.props.item.images.map(img => Object.assign({}, img)))\n\n const uploaderProps = useMemo(() => ({\n view: \"ThumbnailsCollection\",\n buttonId: item.key('campaign_element_photo_upload_button'),\n dropElementId: item.key('campaign_element_drop_zone'),\n modelData: {\n id: 'new',\n name: 'Campaigns::Element',\n attribute: 'photos',\n },\n filesOptions: {\n editableDescription: false,\n editableTitle: false\n },\n files: images,\n pluploadWrapperActions: {\n addNewFiles: (_, files) => imageActions.mergeItemImages(item, files), \n fileUploaded: (_, data) => imageActions.updateItemImage (item, data)\n },\n fileActions: {\n removeFile: (imgId) => imageActions.removeItemImage(item, imgId),\n updateFile: (imgId) => imageActions.updateItemImage\n },\n maxFiles: 1,\n }), [item])\n\n const expandStateAction = { \n currentState: collapseExpandState, \n changeAction: toggleTagsRowVisibility \n }\n\n return (\n <>\n \n \n >\n )\n}\n\nexport default RowItem\n","import * as React from 'react'\nimport RowItem from './preview/row_item'\n\ntype PreviewPropsType = {\n elements: any[]\n removeItem: any\n updateItem: any\n makeCopy: any\n clickTagsSelectorControl: any\n imageActions: any\n}\n\nexport default function Preview(props: PreviewPropsType) {\n const { elements, removeItem, updateItem, makeCopy, clickTagsSelectorControl, imageActions } = props\n const { I18n } = window as any\n return (\n \n \n \n \n {I18n.t('activerecord.models.elements/base_element.one')} ({elements.length})\n | \n {I18n.t('activerecord.attributes.elements/base_element.min_amount')} | \n {I18n.t('activerecord.attributes.elements/base_element.max_amount')} | \n {I18n.t('activerecord.attributes.elements/base_element.dimensions')} | \n {I18n.t('campaigns.others.pdf')} | \n {I18n.t('campaigns.others.note')} | \n {I18n.t('activerecord.attributes.elements/common_element.tags')} | \n {I18n.t('application.actions.title')} | \n
\n \n \n {elements.map((el) => (\n \n ))}\n \n
\n )\n}\n","import React from 'react'\nimport SmModalWindow from '../../shared/modal_window'\nimport Select from 'components/shared/base_react_select'\n\nconst I18n = window.I18n\n\nexport default class TagsModalSelector extends React.Component {\n\n state = { item: null, selected: [] }\n\n onChange = (opts) => {\n this.setState({ item: this.state.item, selected: opts })\n }\n\n onClose = () => this.props.resetActiveItem()\n\n modalContent = () => {\n return (\n \n \n
\n )\n }\n\n clickApply = () => {\n this.props.selectTags(this.state.item, this.state.selected)\n }\n\n modalButtons = () => {\n return (\n \n \n
\n )\n }\n\n static getDerivedStateFromProps (nextProps, prevState) {\n let selected = []\n\n if (nextProps.item) {\n if (prevState.item && nextProps.item.cID.isEqual(prevState.item.cID)) {\n selected = prevState.selected\n }\n else { selected = nextProps.item.tags }\n }\n\n return { item: nextProps.item, selected: selected || [] }\n }\n\n render () {\n const title = I18n.t(\"campaigns.others.select_tags_for\", { name: this.state.item?.name })\n return (\n \n \n
\n )\n }\n}\n","import React, { Fragment } from 'react'\nimport { Button, Form, FormGroup, Label, Input, FormText } from 'reactstrap';\n\nconst I18n = window.I18n\n\nconst initState = (fields) => {\n const [...fieldsKeys] = fields.keys()\n return fieldsKeys.reduce((result, key) => {\n if (!['print_colors', 'product_type'].includes(key)) {\n result[key] = ''\n }\n return result\n }, { print_color_id: '', product_type_id: '' })\n}\n\nclass CreateElementForm extends React.Component {\n\n constructor (props) {\n super(props)\n this.state = initState(this.props.fields)\n }\n\n onChange = (e) => {\n this.setState(Object.assign(this.state, { [e.target.name]: e.target.value }))\n }\n\n onSumbit = () => {\n this.props.createElement(this.state)\n }\n\n static getDerivedStateFromProps(props, state) {\n if (props.needToResetForm) { return initState(props.fields) }\n else return null\n }\n\n render () {\n const { fields } = this.props\n return (\n \n )\n }\n}\n\nexport default CreateElementForm\n","import React, { Fragment } from 'react'\nimport { Button, Modal, ModalHeader, ModalBody, ModalFooter } from 'reactstrap'\nimport Spinner from 'components/shared/spinner'\nimport NewElementForm from './new_element/form'\n\nconst I18n = window.I18n\n\nconst BUTTON_CAPTION = I18n.t('application.actions.create_new', {\n name: I18n.t('activerecord.models.elements/base_element.one').toLowerCase()\n})\n\nclass AddNewElement extends React.Component {\n\n state = { isOpen: false }\n\n toggle = () => this.setState(prevState => ({ isOpen: !prevState.isOpen }))\n close = () => this.setState({ isOpen: false })\n\n render () {\n const { formData, createElement, isFetching } = this.props\n const needToResetForm = !!(this.isFetching && !isFetching)\n this.isFetching = isFetching\n\n return (\n \n \n \n \n {I18n.t('application.actions.create_new', { name: I18n.t('activerecord.models.elements/base_element.one') })}\n { isFetching && }\n \n \n \n \n \n \n )\n }\n}\n\nexport default AddNewElement\n","import React from 'react'\nimport { pick } from 'lodash'\nimport connect from './connect'\nimport CommonElementSelector from './common_element_selector'\nimport LocationElementSelector from './location_element_selector'\nimport Preview from './preview'\nimport SaveButton from '../common/save_button'\nimport SaveAndGoButton from '../common/save_and_go_button'\nimport NextButton from '../common/next_button'\nimport BackButton from '../common/back_button'\nimport TagsModalSelector from './tags_modal_selector'\nimport AddNewElement from './add_new_element'\n\nconst I18n = window.I18n\n\nconst prepareCampaignData = ({ tags }) => {\n return ({\n element_tag_ids: tags.filter(t => t.get('selected')).map(t => (t.get('value') || t.get('id')))\n })\n}\n\nconst splitElementsByGroups = (items) => {\n // notSelectedOther key that mean CommonElement and CampaignElement\n return items.reduce((result, item) => {\n if (item.isSelected) { result.allSelectedElements.push(item) }\n else {\n let typedItemKey = item.isLocation ? 'notSelectedLocationElements' : 'notSelectedOtherElements'\n result[typedItemKey].push(item.selectorAttrs())\n }\n return result\n }, { notSelectedLocationElements: [], notSelectedOtherElements: [], allSelectedElements: [] })\n}\n\nclass ElementsSelector extends React.Component {\n\n handleSave = (e, stepAction = null, next = null) => {\n const campaignElementsData = this.props.elements.reduce((result, el) => {\n if (el.isSelected || el.isMarkedForDestory) { result.push(el.prepareDataForSend()) }\n return result\n }, [])\n\n if (campaignElementsData.length > 0) {\n this.props.saveSelected(this.props.id, prepareCampaignData(this.props), campaignElementsData, stepAction, next)\n }\n }\n\n handleNext = (e) => {\n this.handleSave(e, null, true)\n }\n\n handleBack = () => {\n this.props.stepBack(this.props.id)\n }\n\n saveAndGoToOverview = (e) => {\n this.handleSave(e, 'save_and_go_to_overview')\n }\n\n clickTagsSelectorControl = (item) => {\n this.props.assignActiveItem(item)\n }\n\n resetActiveItem = () => this.props.assignActiveItem(null)\n\n createElement = (data) => {\n this.props.createElement(this.props.id, data)\n }\n\n selectByTags = (tags) => {\n this.props.selectByTags(tags, 'elements')\n }\n\n render () {\n const { selectItems, isFetching, isReady, tags, ByTagsSelector, elements } = this.props\n const previewImageActions = pick(this.props, [\n 'updateItemImageList', 'mergeItemImages', 'addNewImageToItem', 'updateItemImage', 'removeItemImage'\n ])\n const { notSelectedLocationElements, notSelectedOtherElements, allSelectedElements} = splitElementsByGroups(elements)\n\n return (\n \n
\n
\n
\n \n
\n
\n \n
\n
\n
\n
\n \n
\n
\n \n
\n
\n
\n
\n
\n
\n
\n \n
\n
\n \n
\n {isReady && (\n
\n \n
\n )}\n
\n \n
\n
\n
\n )\n }\n}\n\nexport default connect(ElementsSelector)\n","import React from 'react'\nimport store from 'store'\nimport { Provider } from 'react-redux'\nimport ElementsSelector from './elements_selector'\nimport { initElements } from 'actions/campaigns/elements_selector_actions'\nimport { selectByTags } from 'actions/campaigns/common_actions'\n\nimport { setUploaderOptions } from 'actions/shared'\n\nimport ByTagsSelector from './by_tags_selector'\n\nexport default class ElementsSelectorApp extends React.Component {\n\n constructor (props) {\n super(props)\n store.dispatch(initElements({\n id: this.props.id, step: this.props.step,\n isReady: this.props.is_ready,\n elements: this.props.elements,\n tags: this.props.tags,\n elementFormData: this.props.element_form_data\n }))\n\n if (this.props.selectedTags.length) {\n store.dispatch(selectByTags(this.props.selectedTags, 'elements', true))\n }\n }\n\n render () {\n return (\n \n \n \n )\n }\n}\n","import { Fragment } from 'react'\nimport { FormGroup, Label, Input } from 'reactstrap';\n\nconst I18n = window.I18n\n\nexport default ({ onChange, initData, data }) => {\n const { groups } = initData\n const projectGroupId = data['projects']['project_group_id'] || ''\n\n return (\n \n \n \n \n \n { groups.map(group =>\n \n )\n }\n \n \n \n )\n}\n","import { Fragment } from 'react'\nimport { FormGroup, Label, Input } from 'reactstrap';\n\nconst I18n = window.I18n\n\nconst PROJECT_GROUP_NAME_CAPTION = [\n I18n.t('activerecord.models.project_group.one'),\n I18n.t('activerecord.attributes.project_group.name')\n].join(' ')\n\nexport default ({ onChange, initData, data }) => {\n const { workflows } = initData\n const groupWorkflowId = data['group']['workflow_id'] || ''\n const groupName = data['group']['name'] || ''\n return (\n \n \n \n \n \n \n \n \n \n { workflows.map(workflow =>\n \n )\n }\n \n \n \n )\n}\n","import { Fragment } from 'react'\n\nconst I18n = window.I18n\n\nconst SELECT_GROUP_CAPTION = I18n.t('application.actions.select_what', {\n what: I18n.t('activerecord.models.project_group.one').toLowerCase()\n})\n\nconst CREATE_GROUP_CAPTION = [\n I18n.t('application.actions.create'),\n I18n.t('activerecord.models.project_group.one').toLowerCase()\n].join(' ')\n\nexport default ({ onChange, activeFormType }) => {\n\n return (\n \n )\n}\n","import SelectGroupForm from './select_group_form'\nimport CreateGroupForm from './create_group_form'\nimport ViewSelector from './view_selector'\n\nexport const FormViewSelector = ViewSelector\n\nexport default {\n selectGroupForm: SelectGroupForm,\n createGroupForm: CreateGroupForm,\n}\n","import { useState } from 'react'\nimport { Button, Form } from 'reactstrap';\nimport { merge } from 'lodash'\nimport { boxBracketKeyToNestedObjectKey } from 'libs/helpers'\nimport FORMS, { FormViewSelector } from './forms'\n\nconst { I18n } = window\n\nconst isValidFormData = (data, activeFormType) => {\n switch (activeFormType) {\n case 'selectGroupForm':\n return !!data.selectGroupForm.projects?.project_group_id\n case 'createGroupForm':\n const group = data.createGroupForm.group\n return !!(group.name && group.workflow_id)\n }\n}\n\nconst ModalForm = (props) => {\n\n const [initData, updateInitData] = useState({\n selectGroupForm: { groups: props.groups },\n createGroupForm: { workflows: props.workflows }\n })\n const [activeFormType, changeFormType] = useState('selectGroupForm')\n const [formsData, changeFormsData] = useState({\n selectGroupForm: { projects: {} },\n createGroupForm: { group: { name: props.campaignName } }\n })\n\n const [isReadyForSubmit, setNewReadyState] = useState(isValidFormData(formsData, activeFormType))\n\n const switchForm = (e) => changeFormType(e.target.value)\n const updateFormData = (e) => {\n const data = boxBracketKeyToNestedObjectKey({ [e.target.name]: e.target.value })\n changeFormsData(prevData => Object.assign({}, prevData, { [activeFormType]: merge(prevData[activeFormType], data) }))\n }\n\n const validateFormDataResult = isValidFormData(formsData, activeFormType)\n if (validateFormDataResult != isReadyForSubmit) { setNewReadyState(validateFormDataResult) }\n\n const onSumbit = () => props.sendData(props.url, formsData[activeFormType])\n\n const FormView = FORMS[activeFormType]\n\n return (\n \n )\n}\n\nexport default ModalForm\n","import React from 'react'\nimport Spinner from 'components/shared/spinner'\nimport Form from './modal_form'\nimport { Modal, ModalHeader, ModalBody } from 'reactstrap';\n\nconst { I18n, toastr } = window\n\nconst showMessage = () => window.toastr['success'](I18n.t('application.messages.you_will_notified_about_finish'))\n\nconst ModalWindow = ({ isFetching, toggleModal, isOpen, ...otherProps }) => {\n\n const sendData = (url, data) => {\n ajax({url: url, method: 'patch', data: data}).then(resp => {\n if (isOpen) { toggleModal() }\n showMessage()\n })\n }\n\n return (\n \n \n Create projects for campaign elements { isFetching && }\n \n \n \n \n \n )\n}\n\nexport default ModalWindow\n","import { useState } from 'react'\nimport { Button } from 'reactstrap'\nimport ModalWindow from './modal_window'\n\nconst I18n = window.I18n\n\nconst ButtonCaption = [\n I18n.t('application.actions.create'),\n I18n.t('activerecord.models.project_ticket.other')\n].join(' ')\n\nexport default function (props) {\n const [isOpen, toggle] = useState(false)\n\n const toggleModal = () => toggle(prevState => !prevState)\n\n return (\n \n \n \n
\n )\n}\n","import React, { useEffect, useRef } from 'react'\n\nconst I18n = window.I18n\n\nconst UpdateTableBtn = () => {\n const buttonRef = useRef(null)\n\n useEffect(() => {\n const handleClick = (e) => {\n e.preventDefault()\n const form = document.querySelector('[id^=\"edit_campaign\"]')\n if (form) {\n const hiddenInput = document.createElement('input')\n hiddenInput.type = 'hidden'\n hiddenInput.name = 'step_action'\n hiddenInput.value = 'only_save_on_summary'\n\n form.appendChild(hiddenInput)\n\n form.submit()\n }\n }\n\n const button = buttonRef.current\n if (button) {\n button.addEventListener('click', handleClick)\n }\n\n return () => {\n if (button) {\n button.removeEventListener('click', handleClick)\n }\n }\n }, [])\n\n return (\n \n )\n}\n\nexport default UpdateTableBtn\n","import React, { useEffect, useRef } from 'react'\n\nconst I18n = window.I18n\n\nconst NextBtn = () => {\n const buttonRef = useRef(null)\n\n useEffect(() => {\n const handleClick = (e) => {\n e.preventDefault()\n const form = document.querySelector('[id^=\"edit_campaign\"]')\n if (form) {\n const hiddenInput = document.createElement('input')\n hiddenInput.type = 'hidden'\n hiddenInput.name = 'next'\n hiddenInput.value = true\n\n form.appendChild(hiddenInput)\n\n form.submit()\n }\n }\n\n const button = buttonRef.current\n if (button) {\n button.addEventListener('click', handleClick)\n }\n\n return () => {\n if (button) {\n button.removeEventListener('click', handleClick)\n }\n }\n }, [])\n\n return (\n \n )\n}\n\nexport default NextBtn\n","import React from 'react'\nimport WaitingUpdatesChannel from 'acable_channels/waiting_updates'\nimport { Line } from 'rc-progress'\nimport notificationBlocker from 'libs/notifications_blocker'\n\nnotificationBlocker.addCatchFor('campaign_waiting_update')\n\nclass TimeIterator {\n constructor (start, addition = 100) {\n this._addition = addition\n this._value = start\n }\n\n get next() {\n this._value = this._value + this._addition\n return this._value - this._addition\n }\n}\n\nconst timeCounter = new TimeIterator(300, 200)\n\nexport default class WaitProcessing extends React.Component {\n\n constructor (props) {\n super(props)\n this.state = { value: '' }\n this.updatesChannel = new WaitingUpdatesChannel(this.channelData(), this.onReceivedHandler)\n }\n\n onReceivedHandler = (payload) => {\n if (payload.value || payload.value == 0) {\n setTimeout(() => this.setState({ value: payload.value }), timeCounter.next)\n }\n if (payload.location) { setTimeout(() => window.location.href = payload.location, timeCounter.next) }\n }\n\n channelData = () => ({\n channel: 'WaitingUpdatesChannel',\n room: 'Campaign',\n id: this.props.id\n })\n\n checkCampaignState () {\n const { id, step, stepAction } = this.props\n const stepActionParam = stepAction ? `&step_action=${stepAction}` : ''\n ajax({\n url: `/campaigns/${id}/wait-processing.json?step=${step}${stepActionParam}`,\n method: 'get'\n }).then(resp => {\n if (resp.data.location) { this.onReceivedHandler({ value: 100, location: resp.data.location}) }\n })\n }\n\n componentDidMount () {\n this.checkCampaignState()\n }\n\n render () {\n if (this.state.value === '') { return null }\n return (\n \n
\n
{this.state.value}%
\n
\n )\n }\n}\n","import { EventEmitter } from 'fbemitter'\nimport { remove, uniqWith, isEqual, isNumber, debounce } from 'lodash'\nimport { valueBasedOnMinMaxIsValid } from 'views/campaigns/helpers/min_max_validator'\n\nclass AmountValidator {\n constructor (amount, options) {\n this._amount = parseInt(amount) || 0\n this._minAmount = parseInt(options.minAmount) || ''\n this._maxAmount = parseInt(options.maxAmount) || ''\n this._tagsAmount = options.tagsAmount || []\n this._errorsCounter = 0\n }\n\n isSuccess () {\n return !this._errorsCounter\n }\n\n validatedMinMax () {\n const result = valueBasedOnMinMaxIsValid(this._amount, this._minAmount, this._maxAmount)\n\n if (result) { return true }\n\n this._errorsCounter++\n return false\n }\n\n validateTagsAmountsDuplicates () {\n const result = this._tagsAmount.length < 2\n\n if (result) { return true }\n\n this._errorsCounter++\n return false\n }\n\n validateTagsAmountsConflicts () {\n const result = isNumber(this._amount) && (this._tagsAmount.length == 0 || this._tagsAmount.includes(this._amount))\n\n if (result) { return true }\n\n this._errorsCounter++\n return false\n }\n}\n\nclass CampaignAmountsStore extends EventEmitter {\n\n static CONFIG = {\n errorTypes: {\n minMax: 'MinMax',\n tagsAmountsDuplicates: 'TagsAmountsDuplicates',\n tagsAmountsConflicts: 'TagsAmountsConflicts'\n }\n }\n\n constructor () {\n super()\n this._amounts = {}\n this._errors = { minMax: [], tagsAmountsDuplicates: [], tagsAmountsConflicts: [] }\n this._queueProcessedElementIds = []\n }\n\n get config () { return CampaignAmountsStore.CONFIG }\n\n setAmount (elementId, locationId, amount, validateOptions = null) {\n this._queueProcessedElementIds.push(elementId)\n this._amounts[elementId] = Object.assign({}, this._amounts[elementId] || {}, { [locationId]: parseInt(amount) })\n if (validateOptions) {\n this._validate(amount, { elementId, locationId }, validateOptions)\n }\n debounce(() => this._performFinalActions(), 1)()\n }\n\n setNewAmountThroughEvent (elementId, value) {\n Object.keys(this._amounts[elementId] || {}).forEach(locationId => {\n this.emit(`UpdateAmount-${elementId}-${locationId}`, value)\n })\n }\n\n getAmount (elementId, locationId) {\n const elementsAmounts = this._amounts[elementId] || {}\n return elementsAmounts[locationId] || 0\n }\n\n totalFor (elementId) {\n const elementAmounts = this._amounts[elementId] || {}\n\n return Object.values(elementAmounts).reduce((result, val) => result + val, 0)\n }\n\n totalPrintsFor (elementId, pdfPagesCount) {\n const elementAmounts = this._amounts[elementId] || {}\n\n return Object.values(elementAmounts).reduce((result, val) => result + val, 0) * pdfPagesCount\n }\n\n _updatedElementLoationAmount (elementId, locationId) {\n this.emit(`updatedElementLoationAmount-${elementId}-${locationId}`)\n }\n\n _validate (amount, data, options) {\n const validator = new AmountValidator(amount, options)\n\n const errors = []\n\n if (validator.validatedMinMax()) {\n this._removeError('minMax', data)\n } else {\n this._errors.minMax.push(data)\n this._errors.minMax = uniqWith(this._errors.minMax, isEqual)\n errors.push(this.config.errorTypes.minMax)\n }\n\n if (validator.validateTagsAmountsDuplicates()) {\n this._removeError('tagsAmountsDuplicates', data)\n } else {\n this._errors.tagsAmountsDuplicates.push(data)\n this._errors.tagsAmountsDuplicates = uniqWith(this._errors.tagsAmountsDuplicates, isEqual)\n errors.push(this.config.errorTypes.tagsAmountsDuplicates)\n }\n\n if (validator.validateTagsAmountsConflicts()) {\n this._removeError('tagsAmountsConflicts', data)\n } else {\n this._errors.tagsAmountsConflicts.push(data)\n this._errors.tagsAmountsConflicts = uniqWith(this._errors.tagsAmountsConflicts, isEqual)\n errors.push(this.config.errorTypes.tagsAmountsConflicts)\n }\n\n this.emit(`ValidationResult-${data.elementId}-${data.locationId}`, errors)\n }\n\n _removeError (type, { elementId, locationId }) {\n remove(this._errors[type], (err) => (\n err.elementId == elementId && err.locationId == locationId)\n )\n }\n\n _performFinalActions () {\n this._updateTotal()\n this._showErrors()\n }\n\n _updateTotal () {\n while(this._queueProcessedElementIds.length) {\n this.emit(`TotalWasChanged-${this._queueProcessedElementIds.shift()}`)\n }\n }\n\n _showErrors () {\n const { minMax, tagsAmountsDuplicates, tagsAmountsConflicts } = this._errors\n\n this.emit('UpdateViewAmountMessagesArea', this._errors)\n }\n}\n\nexport default new CampaignAmountsStore()\n","import { useState, useCallback } from 'react'\nimport { Button, Modal, ModalHeader, ModalBody, ModalFooter } from 'reactstrap'\n\nconst I18n = window.I18n\n\nconst ControlRow = ({ caption, value, setForAll }) => {\n if (!value) { return null }\n\n return (\n \n {caption} | \n {value} | \n \n \n | \n
\n )\n}\n\nconst ModalWindow = ({ isOpen, toggle, title, min, max, optimalTagAmount, setMinForAll, setMaxForAll, setCustomAmountForAll }) => {\n\n const [customValue, setCustomValue] = useState('')\n\n const changeCustomValue = (e) => setCustomValue(e.target.value)\n\n const applyCustomAmount = useCallback(() => setCustomAmountForAll(customValue), [customValue])\n\n return (\n \n \n {title}\n \n \n \n \n \n )\n}\n\nexport default ModalWindow\n","import { useState } from 'react'\nimport ModalWindow from './modal_window'\n\nconst ChangeAmountControl = (props) => {\n\n const [isOpen, changeOpenState] = useState(false)\n\n const toggle = () => changeOpenState(prevState => !prevState)\n\n return (\n <>\n \n \n >\n )\n}\n\nexport default ChangeAmountControl\n","import { useState, useEffect, useMemo } from 'react'\nimport store from 'stores/campaign_summary_amounts_store'\nimport ChangeAmountControl from 'components/campaigns/summary/change_amount_control'\n\nconst ElementTotalAmount = (props) => {\n\n const { elementId, minAmount, maxAmount, tagsAmount } = props\n const [total, updateTotal] = useState(props.totalValue)\n\n useEffect(() => {\n const listener = store.addListener(`TotalWasChanged-${elementId}`, () => {\n updateTotal(store.totalFor(elementId))\n })\n return () => listener.remove()\n }, [])\n\n const setMinForAll = () => store.setNewAmountThroughEvent(elementId, minAmount)\n const setMaxForAll = () => store.setNewAmountThroughEvent(elementId, maxAmount)\n const setCustomAmountForAll = (val) => store.setNewAmountThroughEvent(elementId, val)\n\n return (\n \n
\n \n
\n
\n {total}\n
\n
\n )\n}\n\nexport default ElementTotalAmount\n","import connect from './connect'\nimport ElementTotalPrints from './element_total_prints'\n\nexport default connect(ElementTotalPrints)\n","import { connect } from 'react-redux'\nimport { setHiddenFields } from 'actions/campaigns/summary_actions'\n\nfunction mapStateToProps (state) {\n return {\n }\n}\n\nfunction mapDispatchToProps (dispatch) {\n return {\n setHiddenFields: (hiddenFields) => dispatch(setHiddenFields(hiddenFields)),\n }\n}\n\nexport default connect(mapStateToProps, mapDispatchToProps)\n\n","import * as React from 'react'\nimport { useState, useEffect } from 'react'\nimport store from '../../../../../stores/campaign_summary_amounts_store'\n\ntype ElementTotalPrintsProps = {\n elementId: number\n pdfPagesCount: number\n totalPrints: number\n}\nexport default function ElementTotalPrints(props: ElementTotalPrintsProps) {\n const { elementId, pdfPagesCount, totalPrints } = props\n const [componentTotalPrints, updateComponentTotalPrints] = useState(totalPrints*pdfPagesCount)\n\n useEffect(() => {\n const listener = store.addListener(`TotalWasChanged-${elementId}`, () => {\n const storeTotalPrints = store.totalPrintsFor(elementId, pdfPagesCount)\n updateComponentTotalPrints(storeTotalPrints)\n })\n return () => listener.remove()\n }, [])\n\n return (\n \n
{componentTotalPrints}
\n
\n )\n}\n","import React from 'react'\nimport store from 'store'\nimport { Provider } from 'react-redux'\nimport ElementTotalPrints from './element_total_prints'\n\nexport default class ElementTotalPrintsApp extends React.Component {\n render() {\n return (\n \n \n \n )\n }\n}\n","import { connect } from 'react-redux'\n\nfunction mapStateToProps (state) {\n return {\n }\n}\n\nfunction mapDispatchToProps (dispatch) {\n return {\n }\n}\n\nexport default connect(mapStateToProps, mapDispatchToProps)\n\n","const AlertOctagonFill = ({ title }) => {\n return (\n \n \n \n )\n}\n\nconst AlertSquareFill = ({ title }) => {\n return (\n \n \n \n )\n}\n\nconst AlertTriangleFill = ({ title }) => {\n return (\n \n \n \n )\n}\n\nexport default {\n AlertOctagonFill, AlertSquareFill, AlertTriangleFill\n}\n","import PropTypes from 'prop-types'\nimport AlertIcons from 'components/campaigns/summary/icons'\nimport store from 'stores/campaign_summary_amounts_store'\nimport { snakeCase } from 'libs/helpers'\n\nconst I18n = window.I18n\n\nconst { AlertOctagonFill, AlertSquareFill, AlertTriangleFill } = AlertIcons\n\nexport const ERROR_TYPE_TO_ICON = {\n [store.config.errorTypes.minMax]: AlertOctagonFill,\n [store.config.errorTypes.tagsAmountsDuplicates]: AlertTriangleFill,\n [store.config.errorTypes.tagsAmountsConflicts]: AlertSquareFill,\n}\n\nconst MinMaxError = ({ count, type }) => {\n if (!count) { return null }\n\n const IconView = ERROR_TYPE_TO_ICON[type]\n\n return (\n \n \n [{count}] \n {I18n.t(`campaigns.error_messages.${snakeCase(type)}`)}\n
\n )\n}\n\nconst TagsAmountsDuplicates = ({ count, type }) => {\n if (!count) { return null }\n\n const IconView = ERROR_TYPE_TO_ICON[type]\n\n return (\n \n \n [{count}] \n {I18n.t(`campaigns.error_messages.${snakeCase(type)}`)}\n
\n )\n}\n\nconst TagsAmountsConflicts = ({ count, type }) => {\n if (!count) { return null }\n\n const IconView = ERROR_TYPE_TO_ICON[type]\n\n return (\n \n \n [{count}] \n {I18n.t(`campaigns.error_messages.${snakeCase(type)}`)}\n
\n )\n\n}\n\nconst ICON_WITH_MSG_PROP_TYPES = {\n count: PropTypes.number.isRequired,\n type: PropTypes.oneOf(Object.values(store.config.errorTypes))\n}\n\nMinMaxError.propTypes = ICON_WITH_MSG_PROP_TYPES\nTagsAmountsDuplicates.propTypes = ICON_WITH_MSG_PROP_TYPES\nTagsAmountsConflicts.propTypes = ICON_WITH_MSG_PROP_TYPES\n\nexport const ERROR_TYPE_TO_ICON_MSG = {\n [store.config.errorTypes.minMax]: MinMaxError,\n [store.config.errorTypes.tagsAmountsDuplicates]: TagsAmountsDuplicates,\n [store.config.errorTypes.tagsAmountsConflicts]: TagsAmountsConflicts,\n}\n","import * as React from 'react'\n\ntype HiddenFieldsProps = {\n id: number\n locationId: number\n campaignElementId: number\n}\nexport default function HiddenFields(props: HiddenFieldsProps) {\n const { id, locationId, campaignElementId } = props\n return (\n <>\n { id && }\n \n \n >\n )\n}\n","export default ({ value, min, max, onChange }) => {\n return (\n \n \n
\n )\n}\n","const I18n = window.I18n\n\nconst Variant = ({ title, val, onChange }) => {\n if (!val) { return null }\n\n const onClick = (v) => onChange({ target: { value: v }})\n\n return (\n \n )\n}\n\nexport default ({ min, max, tagsAmount, onChange }) => {\n return (\n \n \n \n {tagsAmount.map((val, index) => (\n \n )\n )}\n
\n )\n}\n","import * as React from 'react'\nimport { useState, useEffect } from 'react'\nimport classNames from 'classnames'\nimport store from '../../../../../stores/campaign_summary_amounts_store'\nimport { snakeCase } from '../../../../../libs/helpers'\nimport { ERROR_TYPE_TO_ICON } from '../../config'\nimport HiddenFields from './hidden_fields'\nimport AmountInput from './amount_input'\nimport AmountVariants from './amount_variants'\n\nconst { I18n } = window as any\n\nconst ErrorIcon = ({ type }) => {\n const ErrorIconView = ERROR_TYPE_TO_ICON[type]\n\n return (\n \n )\n}\n\nconst listeners = []\n\ntype AmountCellItemProps = {\n minAmount: number\n maxAmount: number\n tagsAmount: number\n elementId: number\n locationId: number\n id: number\n campaignElementId: number\n amount: number\n}\nexport default function AmountCellItem (props: AmountCellItemProps) {\n const { minAmount, maxAmount, tagsAmount, elementId, locationId, id, campaignElementId, amount } = props\n const [componentAmount, updateComponentAmount] = useState(amount)\n const [errors, updateErrors] = useState([])\n\n const updateAmountValue = (e) => {\n const value = parseInt(e.target.value, 10) || 0\n updateComponentAmount(value)\n }\n\n useEffect(() => {\n listeners.push(store.addListener(`ValidationResult-${elementId}-${locationId}`, (validatorErrors) => {\n updateErrors(validatorErrors)\n }))\n listeners.push(store.addListener(`UpdateAmount-${elementId}-${locationId}`, (newAmount) => {\n updateComponentAmount(newAmount)\n }))\n return () => listeners.forEach(listener => listener.remove())\n }, [])\n\n useEffect(() => {\n store.setAmount(elementId, locationId, componentAmount, { minAmount, maxAmount, tagsAmount })\n }, [componentAmount])\n\n const css = classNames(['amount-item', { 'errors': errors.length }])\n return (\n \n
\n
\n
\n
\n {errors.map((type) => )}\n
\n
\n )\n}","import connect from './connect'\nimport AmountCellItem from './amount_cell_item'\n\nexport default connect(AmountCellItem)\n","import React from 'react'\nimport store from 'store'\nimport { Provider } from 'react-redux'\nimport AmountCellItem from './amount_cell_item'\n\nexport default class AmountCellItemApp extends React.Component {\n render() {\n return (\n \n \n \n )\n }\n}\n","import { useState, useEffect } from 'react'\nimport store from 'stores/campaign_summary_amounts_store'\n\nconst TotalPrintsCell = (props) => {\n const { pdfPagesCount, elementId, setHiddenFields } = props\n const [totalPrints, updateTotalPrints] = useState(props.totalPrints)\n\n useEffect(() => {\n const listener = store.addListener(`TotalWasChanged-${elementId}`, () => {\n let totalPrints = store.totalPrintsFor(elementId, pdfPagesCount)\n setHiddenFields({ elementId, totalPrints })\n updateTotalPrints(totalPrints)\n })\n return () => listener.remove()\n }, [])\n\n return (\n \n )\n}\n\nexport default TotalPrintsCell\n","import connect from './connect'\nimport TotalPrintsCell from './total_prints_cell'\n\nexport default connect(TotalPrintsCell)\n","import { connect } from 'react-redux'\nimport { setHiddenFields } from 'actions/campaigns/summary_actions'\n\nfunction mapStateToProps (state) {\n return {\n }\n}\n\nfunction mapDispatchToProps (dispatch) {\n return {\n setHiddenFields: (hiddenFields) => dispatch(setHiddenFields(hiddenFields)),\n }\n}\n\nexport default connect(mapStateToProps, mapDispatchToProps)\n\n","import React from 'react'\nimport store from 'store'\nimport { Provider } from 'react-redux'\nimport TotalPrintsCell from './total_prints_cell'\n\nexport default class TotalPrintsCellApp extends React.Component {\n render() {\n return (\n \n \n \n )\n }\n}\n","import { useState , useEffect } from 'react'\nimport store from 'stores/campaign_summary_amounts_store'\nimport { ERROR_TYPE_TO_ICON_MSG } from 'components/campaigns/summary/config'\n\nconst ErrorIconMsg = ({ type, count }) => {\n if (!count) { return null }\n\n const ErrorIconMsgView = ERROR_TYPE_TO_ICON_MSG[type]\n\n return (\n \n \n
\n )\n}\n\nconst AmountsMessagesArea = ({}) => {\n\n const [errorsCounts, updateErrorsCounts] = useState({ MinMax: 0, TagsAmountsConflicts: 0, TagsAmountsDuplicates: 0 })\n\n useEffect(() => {\n const listener = store.addListener('UpdateViewAmountMessagesArea', (errors) => {\n const { minMax, tagsAmountsDuplicates, tagsAmountsConflicts } = errors\n updateErrorsCounts({\n MinMax: minMax.length,\n TagsAmountsDuplicates: tagsAmountsDuplicates.length,\n TagsAmountsConflicts: tagsAmountsConflicts.length\n })\n })\n return () => listener.remove()\n }, [])\n\n return (\n \n { Object.keys(errorsCounts).map(type => ) }\n
\n )\n}\n\nexport default AmountsMessagesArea\n","import LocationsSelectorApp from './locations_selector_app'\nimport ElementsSelectorApp from './elements_selector_app'\nimport CreateProjectsBtn from './create_projects_btn'\nimport UpdateTableBtn from './update_table_btn'\nimport NextBtn from './next_btn'\nimport WaitProcessing from './wait_processing'\nimport Summary from './summary'\n\nexport default {\n LocationsSelectorApp,\n ElementsSelectorApp,\n Summary,\n CreateProjectsBtn,\n UpdateTableBtn,\n NextBtn,\n WaitProcessing\n}\n","import ElementTotalAmount from './element_total_amount'\nimport ElementTotalPrintsApp from './element_total_prints_app'\nimport AmountCellApp from './amount_cell_item_app'\nimport TotalPrintsCellApp from './total_prints_cell_app'\nimport AmountsMessagesArea from './amounts_messages_area'\n\nexport default {\n AmountCellApp,\n TotalPrintsCellApp,\n ElementTotalAmount,\n ElementTotalPrintsApp,\n AmountsMessagesArea\n}\n","import { connect } from 'react-redux'\nimport { getItems, createPostorderWithItems, updateItem } from 'actions/campaign_postorders/create_postorder_form'\n\nexport default connect((state) => ({\n campaignId: state.getIn(['campaign_postorders', 'campaignId']),\n locationId: state.getIn(['campaign_postorders', 'locationId']),\n locations: state.getIn(['campaign_postorders', 'locations']),\n items: state.getIn(['campaign_postorders', 'items'])\n}), { getItems, updateItem, createPostorderWithItems })\n","import React from 'react'\nimport ImageList from 'components/uploader_v2_app/views/thumbnails_collection/image_list'\n\nexport default class RowItem extends React.Component {\n\n onChange = (e) => {\n this.props.updateItem(this.props.campaignElementId, { extraAmount: e.target.value })\n }\n\n itemAttrs = () => ({\n name: this.props.item.get('elementName'),\n extraAmount: this.props.item.get('extraAmount'),\n orderedAmount: this.props.item.get('orderedAmount'),\n locationElementAmount: this.props.item.get('locationElementAmount')\n })\n\n render () {\n const { name, locationElementAmount, orderedAmount, extraAmount } = this.itemAttrs()\n return (\n \n {name} | \n \n \n | \n {locationElementAmount} | \n {orderedAmount} | \n \n \n | \n
\n )\n }\n}\n","import React from 'react'\nimport RowItem from './row_item'\n\nconst I18n = window.I18n\n\nexport default class TableWithExtraAmountInputs extends React.Component {\n\n render () {\n return (\n \n \n \n {I18n.t('activerecord.models.elements/base_element.one')} | \n {I18n.t('campaigns.others.thumbnail')} | \n {I18n.t('campaign_postorders.table_heads.location_element_amount')} | \n {I18n.t('campaign_postorders.table_heads.ordered_amount')} | \n {I18n.t('campaign_postorders.table_heads.order_extra')} | \n
\n \n \n { this.props.items.map( (item) =>\n \n )}\n \n
\n )\n }\n}\n","import React from 'react'\nimport Select from 'components/shared/base_react_select'\nimport connect from './connect'\nimport TableWithExtraAmountInputs from './table_with_extra_amount_inputs'\nimport classNames from 'classnames'\n\nclass CreatePostorderForm extends React.Component {\n\n onChange = (opt) => {\n this.props.getItems(this.props.campaignId, opt.value)\n }\n\n onClick = () => {\n this.props.createPostorderWithItems(this.props.campaignId, this.props.locationId, this.props.items)\n }\n\n render () {\n const options = this.props.locations.toJS()\n return (\n \n
\n
\n
\n
\n
\n
\n
\n \n
\n
\n
\n )\n }\n}\n\nexport default connect(CreatePostorderForm)\n","import React from 'react'\nimport store from 'store'\nimport { Provider } from 'react-redux'\nimport CreatePostorderForm from './create_postorder_form'\nimport { initBaseState } from 'actions/campaign_postorders/create_postorder_form'\n\nexport default class CreatePostorderApp extends React.Component {\n\n constructor (props) {\n super(props)\n store.dispatch(initBaseState(props))\n }\n\n render () {\n return (\n \n \n \n )\n }\n}\n","import CreatePostorderApp from './create_postorder_app'\n\nexport default {\n CreatePostorderApp\n}\n","import { Button, Modal, ModalHeader, ModalBody, ModalFooter } from 'reactstrap';\n\n export default ({ header, children, isOpen, scrollable, onClose }) => {\n if (!isOpen) { return null }\n\n return (\n \n {header}\n {children}\n \n )\n}\n","import ModalWindow from './modal_window'\n\nconst I18n = window.I18n\n\nconst ModalWithProjects = ({ name, projects, isOpen, closeModal }) => {\n if (projects.length == 0) { return null }\n\n return (\n \n \n \n \n {I18n.t('activerecord.attributes.project_ticket.location')} | \n {I18n.t('activerecord.attributes.project_ticket.title')} | \n {I18n.t('activerecord.attributes.project_ticket.created_at')} | \n
\n \n \n { projects.map((project) => (\n \n {project.location_name} | \n {project.title} | \n {project.created_at} | \n
\n ))\n }\n \n
\n \n )\n}\n\nexport default ModalWithProjects\n","import React, { Component } from 'react'\nimport PropTypes from 'prop-types'\n\nconst { I18n } = window\n\nconst ProjectStatusName = ({ name }) => {\n return (\n {I18n.t(`activerecord.enums.project_status.name.${name}`)}
\n )\n}\n\nexport default class Status extends Component {\n static propTypes = {\n status: PropTypes.object.isRequired,\n items: PropTypes.array.isRequired,\n }\n\n count = () => {\n const statusName = this.props.status.name\n\n // replace hash actual for mspWorkflow item\n return this.props.items.reduce((sum, item) => {\n const _sumVal = item[statusName.replace('_count', '_sum')]\n return sum + ( _sumVal || _sumVal == 0 ? _sumVal : item[statusName])\n }, 0)\n }\n\n onClick = () => {\n this.props.assignStatus(this.props.status)\n }\n\n render () {\n const { status } = this.props\n \n return (\n \n
{this.count()}
\n
{status.title()}
\n
\n {status.included.map(status_item =>
)}\n
\n
\n )\n }\n}\n","import Status from './status'\n\nexport default Status\n","const CONFIG = {\n statuses: [\n {\n name: 'not_yet_started_count',\n scope: 'not_started',\n color: '#B13827',\n title: () => I18n.t('overview.components.statuses.not_yet_started'),\n included: ['initiated', 'unscheduled', 'on_hold'],\n secondaryColor: 'white',\n },\n {\n name: 'ongoing_count',\n scope: 'ongoing',\n color: '#F1CF29',\n title: () => I18n.t('overview.components.statuses.ongoing'),\n included: ['in_progress', 'offer_accepted', 'rescheduled', 'booked', 'overdue', 'open', 'design_approved'],\n secondaryColor: 'black',\n },\n {\n name: 'action_required_count',\n scope: 'action_required',\n color: '#4C7D75',\n title: () => I18n.t('overview.components.statuses.action_required'),\n included: ['pending_assessment', 'design_pending_approval', 'offer_pending_approval', 'approved', 'rejected'],\n secondaryColor: 'black',\n },\n {\n name: 'completed_count',\n scope: 'completed',\n color: '#b2b0b0',\n title: () => I18n.t('overview.components.statuses.completed'),\n included: ['completed'],\n secondaryColor: 'black',\n },\n ],\n}\n\nconst statusKeys = CONFIG.statuses.reduce((result, status) => {\n result.push(status.name.replace('_count', ''))\n return result\n}, [])\n\nCONFIG.statusKeys = statusKeys\n\nexport default CONFIG\n","import React, { Component } from 'react'\nimport PropTypes from 'prop-types'\nimport Status from './status'\nimport config from '../../config'\n\nexport default class StatusSummary extends Component {\n static propTypes = {\n projectGroups: PropTypes.array.isRequired,\n mspWorkflows: PropTypes.array.isRequired,\n }\n\n state = { status: null }\n\n assignStatus = (status = null) => {\n this.setState((prevState) => Object.assign({}, prevState, { status }))\n if (status) {\n const { projectGroups, mspWorkflows, getProjects } = this.props\n getProjects({\n project_group_ids: projectGroups.map((g) => g.id),\n msp_workflow_ids: mspWorkflows.map((mspw) => mspw.id),\n status: status.scope,\n })\n }\n }\n\n closeModal = () => this.setState({ status: null })\n\n render() {\n const { projectGroups, mspWorkflows, projects, ModalWithProjects } = this.props\n const items = projectGroups.concat(mspWorkflows)\n\n return (\n \n
\n
\n {config.statuses.map((status, i) => (\n \n ))}\n
\n
\n )\n }\n}\n","import StatusSummary from './status_summary'\nimport connect from './connect'\n\nexport default connect(StatusSummary)\n","import { connect } from 'react-redux'\nimport { getProjects } from 'actions/overview_actions'\n\nexport default connect(state => {\n return {\n projectGroups: state.getIn(['overview', 'projectGroups']).toJS(),\n mspWorkflows: state.getIn(['overview', 'mspWorkflows']).toJS(),\n projects: state.getIn(['overview', 'projects']).toJS()\n }\n}, { getProjects })\n","import { Component } from 'react'\nimport PropTypes from 'prop-types'\nimport classNames from 'classnames'\n\nexport default class Comment extends Component {\n static propTypes = {\n comment: PropTypes.object.isRequired,\n }\n\n onClick = () => {\n this.props.assignComment(this.props.comment.toJS())\n } \n\n render () {\n const comment = this.props.comment\n return (\n \n \n \n | \n \n \n {comment.get('body')}\n \n | \n \n {comment.get('company_name')} \n {comment.get('project_id')}\n | \n
\n )\n }\n}\n","import Comment from './comment'\n\nexport default Comment\n","import { Component } from 'react'\nimport PropTypes from 'prop-types'\nimport Comment from './comment'\n\nconst I18n = window.I18n\n\nconst Translations = {\n attachments: I18n.t('activerecord.models.attachments/base.other').toLowerCase(),\n goToComment: I18n.t('application.actions.goto', {\n item: I18n.t('activerecord.models.comment.one').toLowerCase()\n })\n}\n\nexport default class CommentList extends Component {\n static propTypes = {\n comments: PropTypes.object.isRequired,\n }\n\n state = { comment: null }\n\n closeModal = () => {\n this.assignComment(null)\n }\n\n assignComment = (comment = null) => {\n this.setState({ comment })\n }\n\n render () {\n const { comments, ModalWindow } = this.props\n const { id, body, attachment_count, meta, company_name, project_title, link } = this.state.comment || {}\n\n return (\n \n
\n \n
\n
\n {body}\n {attachment_count && `(${attachment_count} ${Translations.attachments})`}\n
\n
\n {meta} {Translations.goToComment}\n \n
\n
\n \n
\n \n { comments.map(comment => (\n \n ))\n }\n \n
\n
\n )\n }\n}\n","import { connect } from 'react-redux'\n\nfunction mapStateToProps (state) {\n return {\n comments: state.getIn(['overview', 'comments']),\n }\n}\n\nfunction mapDispatchToProps (dispatch) {\n return {\n }\n}\n\nexport default connect(mapStateToProps, mapDispatchToProps)\n","import CommentList from './comment_list'\nimport connect from './connect'\n\nexport default connect(CommentList)\n","import { Component } from 'react'\nimport PropTypes from 'prop-types'\nimport classNames from 'classnames'\n\nexport default class StatusChange extends Component {\n static propTypes = {\n statusChange: PropTypes.object.isRequired,\n }\n\n onClick = () => {\n this.props.assignChange(this.props.statusChange)\n }\n\n render () {\n const { statusChange } = this.props\n return (\n \n \n \n | \n \n {`${statusChange.get('old_status')} --> ${statusChange.get('new_status')}`}\n | \n \n {statusChange.get('location_name')}\n | \n \n {statusChange.get('company_name')} \n {statusChange.get('project_id')}\n | \n
\n )\n }\n}\n","import StatusChange from './status_change'\n\nexport default StatusChange\n","import { Component } from 'react'\nimport PropTypes from 'prop-types'\nimport StatusChange from './status_change'\n\nconst I18n = window.I18n\n\nexport default class StatusChangeList extends Component {\n static propTypes = {\n statusChanges: PropTypes.object.isRequired,\n }\n\n state = { change: null }\n\n closeModal = () => {\n this.assignChange()\n }\n\n assignChange = (change = null) => {\n this.setState({ change })\n }\n\n render () {\n const { ModalWindow, statusChanges } = this.props\n const { id, company_name, new_status, old_status, project_title, meta } = this.state.change || {}\n\n return (\n \n
\n \n \n \n {I18n.t('application.titles.from')} | \n {I18n.t('application.titles.to')} | \n
\n \n \n \n {old_status} | \n {new_status} | \n
\n \n
\n {meta}\n \n
\n \n { statusChanges.map(status => (\n \n ))\n }\n \n
\n
\n )\n }\n}\n","import { connect } from 'react-redux'\n\nfunction mapStateToProps (state) {\n return {\n statusChanges: state.getIn(['overview', 'statusChanges']),\n }\n}\n\nfunction mapDispatchToProps (dispatch) {\n return {\n }\n}\n\nexport default connect(mapStateToProps, mapDispatchToProps)\n","import StatusChangeList from './status_change_list'\nimport connect from './connect'\n\nexport default connect(StatusChangeList)\n","import { Component } from 'react'\nimport PropTypes from 'prop-types'\nimport { Doughnut, Bar } from 'react-chartjs-2'\nimport config from '../../../config'\n\nconst CHART_OPTIONS = {\n responsive: true,\n legend: {\n position: 'bottom',\n }\n}\n\nexport default class ProjectGroup extends Component {\n static propTypes = {\n projectGroup: PropTypes.object.isRequired,\n }\n\n onElementsClick = ([element, _]) => {\n if (element) {\n const status = element._chart.options.meta.statusKeys[element._index]\n this.props.getProjects({ group_id: this.props.projectGroup.id, status })\n this.props.assignGroup(this.props.projectGroup)\n }\n }\n\n renderChart () {\n const projectGroup = this.props.projectGroup\n\n const chartOptionsWithMeta = Object.assign({}, CHART_OPTIONS, { meta: { statusKeys: config.statusKeys } })\n return (\n \n )\n }\n\n buildChartData () {\n return {\n labels: this.buildLabels(),\n datasets: [{\n data: this.buildData(),\n backgroundColor: this.buildColors(),\n hoverBackgroundColor: this.buildColors(),\n }]\n }\n }\n\n buildLabels = () => config.statuses.map(status => status.title())\n buildColors = () => config.statuses.map(status => status.color)\n buildData = () => config.statuses.map(status => this.props.projectGroup[status.name])\n\n render () {\n const { projectGroup} = this.props\n return (\n \n
\n
\n
\n {this.renderChart()}\n
\n {projectGroup.comments_count}\n \n
\n
\n

\n
\n
\n
\n
\n )\n }\n}\n","import ProjectGroup from './project_group'\n\nexport default ProjectGroup\n","import * as React from 'react'\nimport { Component, Fragment } from 'react'\nimport PropTypes from 'prop-types'\nimport ProjectGroup from './project_group'\n\nexport default class ProjectGroupList extends Component {\n static propTypes = { projectGroups: PropTypes.array.isRequired }\n\n render () {\n const { projectGroups, getProjects, assignGroup } = this.props\n\n return (\n \n { this.props.projectGroups.map(projectGroup => (\n \n ))\n }\n \n )\n }\n}\n","import ProjectGroupList from './project_group_list'\n\nexport default ProjectGroupList\n","import React from 'react'\nimport { HorizontalBar } from 'react-chartjs-2'\nimport config from '../../config'\n\nconst tooltipCustomTitle = (workflow, tooltipItems, data) => {\n const item = tooltipItems[0]\n return `[${item.yLabel}] ${workflow.steps_names[item.index]}`;\n}\n\nconst options = {\n responsive: true,\n tooltips: {\n enabled: true\n },\n hover :{\n animationDuration:0\n },\n scales: {\n xAxes: [{\n ticks: {\n beginAtZero:true,\n },\n scaleLabel:{\n display:false\n },\n gridLines: {\n },\n stacked: true\n }],\n yAxes: [{\n gridLines: {\n display:false,\n color: \"#fff\",\n zeroLineColor: \"#fff\",\n zeroLineWidth: 0\n },\n stacked: true\n }]\n },\n legend:{\n position: 'bottom',\n }\n}\n\n\nexport default class StackedBarGraph extends React.Component {\n\n graphData = (workflow) => {\n return {\n labels: workflow.labels,\n datasets: [{\n label: config.statuses[0].title(),\n backgroundColor: config.statuses[0].color,\n data: workflow.not_yet_started_count\n },\n {\n label: config.statuses[1].title(),\n backgroundColor: config.statuses[1].color,\n data: workflow.ongoing_count\n },\n {\n label: config.statuses[2].title(),\n backgroundColor: config.statuses[2].color,\n data: workflow.action_required_count\n },\n {\n label: config.statuses[3].title(),\n backgroundColor: config.statuses[3].color,\n data: workflow.completed_count\n },\n ]}\n }\n\n getElementAtEvent = ([element, _]) => {\n const status = element._chart.options.meta.statusKeys[element._datasetIndex]\n const stepId = this.props.workflow.steps_ids[element._index]\n this.props.getProjects({ multi_step_workflow_step_id: stepId, status })\n this.props.assignMspWorkflow(Object.assign({}, this.props.workflow, {workflow_step_id: stepId }))\n }\n\n extendedOptions = (workflow) => {\n return Object.assign({}, options, {\n tooltips: {\n callbacks: {\n title: (tooltipItems, data) => tooltipCustomTitle(workflow, tooltipItems, data)\n }\n }, meta: { statusKeys: config.statusKeys }\n })\n }\n\n render () {\n const { workflow } = this.props\n\n return (\n \n
\n
\n
\n
\n

\n
\n
\n \n
\n
\n
\n
\n )\n }\n\n}\n","import React, { Fragment } from 'react'\nimport StackedBarGraph from './stacked_bar_graph'\n\nclass MspWorkflowsGraphs extends React.Component {\n\n render () {\n const { mspWorkflows, getProjects, assignMspWorkflow } = this.props\n\n return (\n \n { mspWorkflows.map((mspWorkflow) =>\n \n )\n }\n \n )\n }\n}\n\nexport default MspWorkflowsGraphs\n","import { connect } from 'react-redux'\nimport { getProjects } from 'actions/overview_actions'\n\nexport default connect((state) => ({\n mspWorkflows: state.getIn(['overview', 'mspWorkflows']).toJS(),\n projectGroups: state.getIn(['overview', 'projectGroups']).toJS(),\n projects: state.getIn(['overview', 'projects']).toJS()\n}),{ getProjects })\n","import React from 'react'\nimport ProjectGroupsGraphs from './project_group_list'\nimport MspWorkflowsGraphs from './msp_workflows_graphs'\nimport connect from './graphs_connect'\nimport classNames from 'classnames'\n\nconst I18n = window.I18n\n\nclass Graphs extends React.Component {\n\n state = { group: null, workflow: null }\n\n assignGroup = (group = null) => this.setState(prevState => Object.assign({}, prevState, { group }))\n\n assignMspWorkflow = (workflow = null) => this.setState(prevState => Object.assign({ }, prevState, { workflow }) )\n\n closeModal = () => this.setState({ group: null, workflow: null })\n\n render () {\n const { containerClassName, mspWorkflows, projectGroups, ModalWithProjects, projects, getProjects } = this.props\n\n if (mspWorkflows.size == 0 && projectGroups.size == 0) {\n return (\n \n
{I18n.t('overview.components.empty_message')}
\n
\n )\n }\n\n const { group, workflow } = this.state\n\n return (\n \n )\n }\n}\n\nexport default connect(Graphs)\n","import React, { Component } from 'react'\nimport ModalWindow from './modal_window'\nimport ModalWithProjects from './modal_with_projects'\nimport StatusSummary from './status_summary'\nimport CommentList from './comment_list'\nimport StatusChangeList from './status_change_list'\nimport Graphs from './graphs'\n\nexport default class Overview extends Component {\n render () {\n return (\n \n
\n
\n
\n \n \n {I18n.t('overview.components.recent_comments')} | \n {I18n.t('overview.components.last_status_updates')} | \n
\n \n \n \n | \n | \n
\n \n
\n
\n
\n
\n )\n }\n}\n","import Overview from './overview'\n\nexport default Overview\n","import { Component } from 'react'\nimport store from 'store'\nimport { Provider } from 'react-redux'\nimport Overview from './overview'\n\nexport default class OverviewApp extends Component {\n render () {\n return (\n \n \n \n )\n }\n}\n","import React from 'react'\n\nfunction ChartHeader({ months, monthNames, weekdayNames }) {\n return (\n <>\n \n | \n | \n {Object.keys(months).map(yearMonth => {\n const [year, month] = yearMonth.split(',')\n return (\n \n {monthNames[month]}\n | \n )\n })}\n
\n \n | \n | \n {Object.keys(months).map(yearMonth => {\n return months[yearMonth].map(day => {\n return {day.date()} | \n })\n })}\n
\n \n Firma | \n Start | \n Finish | \n {Object.keys(months).map(yearMonth => {\n return months[yearMonth].map(day => {\n return {weekdayNames[day.isoWeekday() - 1][0]} | \n })\n })}\n
\n >\n )\n}\n\nexport default ChartHeader\n","import React from 'react'\nimport { isNil } from 'lodash'\n\nconst DATE_FORMAT = 'DD-MM-YYYY'\n\nfunction ChartRow({ name, startDate, endDate, installationDate, range }) {\n return (\n \n {name} | \n {startDate.format(DATE_FORMAT)} | \n {endDate && endDate.format(DATE_FORMAT)} | \n {range.map(day => {\n const cellClass = day.isBetween(startDate, endDate, 'day', '[]') ? 'filled' : ''\n return (\n \n {!isNil(installationDate) && day.isSame(installationDate, 'day') && (\n •\n )}\n {!isNil(startDate) && isNil(endDate) && day.isSame(startDate, 'day') && (\n ⧫\n )}\n | \n )\n })}\n
\n )\n}\n\nexport default ChartRow\n","import React from 'react'\nimport Moment from 'moment'\nimport { extendMoment } from 'moment-range'\nimport { groupBy, isNull } from 'lodash'\nimport ChartHeader from './chart_header'\nimport ChartRow from './chart_row'\n\nconst moment = extendMoment(Moment)\n\nfunction startDateFromProjects(projects) {\n const startDates = projects.map(p => p.start_date).filter(d => !isNull(d))\n if (!startDates.length) {\n const createdDates = projects.map(p => moment(p.created_at))\n return moment.min(createdDates).startOf('day')\n }\n return moment.min(startDates).startOf('day')\n}\n\nfunction endDateFromProjects(projects, startDate) {\n const endDates = projects.map(p => (isNull(p.deadline) ? null : moment(p.deadline))).filter(d => !isNull(d))\n if (!endDates.length) {\n const installerVisitDates = projects\n .map(p => (isNull(p.installer_visit_date) ? null : moment(p.installer_visit_date)))\n .filter(d => !isNull(d))\n\n if (!installerVisitDates.length) {\n startDate\n .clone()\n .add(1, 'week')\n .startOf('day')\n }\n return moment.max(installerVisitDates).startOf('day')\n }\n return moment.max(endDates).startOf('day')\n}\n\nfunction ProjectChart(props) {\n const projects = props.projects\n const projectGroups = props.project_groups\n const monthNames = props.month_names\n const weekdayNames = props.weekday_names\n\n const startDate = startDateFromProjects(projects)\n const endDate = endDateFromProjects(projects, startDate)\n\n const range = Array.from(\n moment()\n .range(startDate, endDate)\n .by('day', { excludeEnd: false })\n )\n const months = groupBy(range, date => [date.year(), date.month()])\n return (\n \n
\n \n \n \n \n {projectGroups.map(group => {\n const groupProjects = projects.filter(p => p.project_group_id === group.id)\n const groupStartDate = startDateFromProjects(groupProjects)\n const groupEndDate = endDateFromProjects(groupProjects, groupStartDate)\n\n return (\n \n \n {groupProjects.map(project => {\n const installationDate = project.installer_visit_date ? moment(project.installer_visit_date) : null\n const deadline = project.deadline ? moment(project.deadline) : null\n return (\n \n )\n })}\n \n )\n })}\n \n
\n
\n )\n}\n\nexport default ProjectChart\n","import ProjectsChart from './projects_chart'\n\nexport default ProjectsChart\n","import React from 'react'\nimport Toggle from 'react-toggle'\nimport \"react-toggle/style.css\"\n\nconst ajax = window.ajax\nconst I18n = window.I18n\n\nfunction CheckedToggleButton (props) {\n return (\n \n \n {I18n.t('campaigns.documents.state.approved')}\n
\n )\n}\n\nfunction UnCheckedToggleButton (props) {\n return (\n \n \n {I18n.t('campaigns.documents.state.not_set')}\n
\n )\n}\n\nexport default class ToggleButton extends React.Component {\n\n constructor (props) {\n super(props)\n this.state = { [this.props.name]: this.props.checked }\n }\n\n isChecked = () => this.state[this.props.name]\n\n getToggleViewBasedOnState = () => {\n if (this.isChecked()) {\n return CheckedToggleButton\n }\n else return UnCheckedToggleButton\n }\n\n onChange = () => {\n const data = { format: 'json' }\n data[this.props.name] = !this.isChecked()\n ajax({\n url: this.props.url,\n method: 'patch',\n data: data\n }).then( (resp) => {\n const newState = { }\n newState[this.props.name] = resp.data.checked\n this.setState(newState)\n })\n }\n\n render () {\n const ToggleView = this.getToggleViewBasedOnState()\n return (\n \n \n
\n )\n }\n}\n","import { connect } from 'react-redux'\nimport { types } from 'actions/projects/msp_step_button_actions'\nimport { createLoadingSelector } from 'libs/requests_status_handlers'\n\nconst loadingSelector = createLoadingSelector([\n types.CREATE_MSP_PROJECT,\n types.LOAD_MSP_PROJECT_STEPS\n])\n\nexport default connect((state) => ({\n isFetching: loadingSelector(state)\n}), { })\n","import { connect } from 'react-redux'\nimport {\n createMspProject, loadStepsForMspProject, assignStep\n} from 'actions/projects/msp_step_button_actions'\n\nexport default connect((state) => ({\n current: state.getIn(['projects', 'mspData', 'current']),\n projects: state.getIn(['projects', 'mspData', 'projects']),\n workflows: state.getIn(['projects', 'mspData', 'workflows']),\n steps: state.getIn(['projects', 'mspData', 'steps'])\n}), { createMspProject, loadStepsForMspProject, assignStep })\n","import React from 'react'\nimport { Button, Form, FormGroup, Label, Input, FormText } from 'reactstrap';\n\nconst I18n = window.I18n\n\nconst isValidFormData = (data) => data.mspProjectId && data.workflowStepId\n\nclass AssignForm extends React.Component {\n\n constructor (props) {\n super(props)\n this.state = {\n mspProjectId: this.props.current.get('mspProjectId'),\n workflowStepId: this.props.current.get('workflowStepId')\n }\n }\n\n onProjectSelect = (e) => {\n const mspProjectId = e.target.value\n this.setState(Object.assign(this.state, { mspProjectId: mspProjectId }))\n if (parseInt(mspProjectId)) { this.props.loadStepsForMspProject(mspProjectId) }\n }\n\n onStepSelect = (e) => {\n this.setState(Object.assign(this.state, { workflowStepId: e.target.value }))\n }\n\n onSumbit = (e) => {\n if (this.state.workflowStepId == this.props.current.get('workflowStepId')) {\n this.props.toggleModal()\n }\n else this.props.assignStep(\n this.props.current.get('projectId'),\n this.state.mspProjectId,\n this.state.workflowStepId\n )\n }\n\n render () {\n const { mspProjects, mspSteps, current } = this.props\n const isReadyForSubmit = isValidFormData(this.state)\n return (\n \n )\n }\n}\n\nexport default AssignForm\n","import React from 'react'\nimport { Button, Form, FormGroup, Label, Input, FormText } from 'reactstrap';\n\nconst I18n = window.I18n\n\nconst isValidFormData = (data) => data.name && data.workflow\n\nconst CLEAR_FORM_STATE = { name: '', workflow: '' }\n\nclass CreateForm extends React.Component {\n\n constructor (props) {\n super(props)\n this.state = Object.assign({}, CLEAR_FORM_STATE)\n }\n\n onFormInputChange = (e) => {\n this.setState(Object.assign(this.state, { [e.target.name]: e.target.value }))\n }\n\n onSumbit = () => {\n this.props.createMspProject(\n this.props.current.get('projectId'),\n { name: this.state.name, workflow_id: this.state.workflow }\n )\n this.setState(Object.assign({}, CLEAR_FORM_STATE))\n }\n\n render () {\n const { mspWorkflows, current } = this.props\n const isReadyForSubmit = isValidFormData(this.state)\n return (\n \n )\n }\n}\n\nexport default CreateForm\n","import React from 'react'\nimport connect from './connect'\nimport { TabContent, TabPane, Nav, NavItem, NavLink, Row, Col } from 'reactstrap';\nimport classnames from 'classnames';\nimport AssignForm from './assign_form'\nimport CreateForm from './create_form'\n\nconst I18n = window.I18n\n\nclass Forms extends React.Component {\n\n state = { activeTab: '1' }\n\n toggle = (tab) => this.setState({ activeTab: tab })\n\n render () {\n return (\n \n
\n
\n \n \n \n \n \n
\n \n \n \n \n \n \n
\n \n \n
\n )\n }\n}\n\nexport default connect(Forms)\n","import React from 'react'\nimport connect from './modal_window_connect'\nimport Spinner from 'components/shared/spinner'\nimport Forms from './forms'\nimport { Button, Modal, ModalHeader, ModalBody, ModalFooter } from 'reactstrap';\n\nconst I18n = window.I18n\n\nclass ModalWindow extends React.Component {\n render () {\n const { isFetching } = this.props\n\n return (\n \n \n {I18n.t('multi_step.project_step_button.modal_title')} { isFetching && }\n \n \n \n \n \n )\n }\n}\n\nexport default connect(ModalWindow)\n","import React from 'react'\nimport store from 'store'\nimport { Provider } from 'react-redux'\nimport { Button } from 'reactstrap'\nimport ModalWindow from './modal_window'\nimport { init } from 'actions/projects/msp_step_button_actions'\n\nclass MspStepButtonApp extends React.Component {\n\n constructor (props) {\n super(props)\n store.dispatch(init(this.props))\n this.state = { modalFormIsActive: false }\n }\n\n toggleModal = () => {\n this.setState(prevState => ({\n modalFormIsActive: !prevState.modalFormIsActive\n }))\n }\n\n render () {\n return (\n \n \n \n \n
\n \n )\n }\n}\n\nexport default MspStepButtonApp\n","export default ({ title, onClick }) => {\n\n return (\n \n \n
\n )\n}\n","import { connect } from 'react-redux'\nimport { types, updateAttachable } from 'actions/uploader_actions'\nimport { createLoadingSelector } from 'libs/requests_status_handlers'\n\nimport { VCSpinner } from 'components/shared/spinner'\nimport Uploader from 'components/uploader_v2_app/uploader'\nimport FilesThrower from './files_thrower'\n\nconst I18n = window.I18n\n\nconst ProjectUploads = (props) => {\n\n const { buttonId, targetKey, updateAttachable, isReqStateInProgress } = props\n\n const onClick = () => updateAttachable(buttonId, targetKey)\n\n return (\n \n
\n \n {I18n.t('project_tickets.titles.project_uploads')}\n \n
\n
\n \n \n )\n}\n\nconst isReqStateInProgress = createLoadingSelector([types.UPDATE_ATTACHABLE])\n\nexport default connect((state, props) => {\n const uploaderData = state.getIn(['uploader', props.buttonId])?.toJS() || {}\n return ({\n isReqStateInProgress: isReqStateInProgress(state) || uploaderData.state == 'uploading',\n ...(state.getIn(['uploader', props.buttonId])?.toJS() || {})\n })\n}, { updateAttachable })(ProjectUploads)\n","import { connect } from 'react-redux'\nimport { types, updateAttachable } from 'actions/uploader_actions'\nimport { createLoadingSelector } from 'libs/requests_status_handlers'\n\nimport { VCSpinner } from 'components/shared/spinner'\nimport Uploader from 'components/uploader_v2_app/uploader'\nimport FilesThrower from './files_thrower'\n\nconst I18n = window.I18n\n\nconst SharedUploads = (props) => {\n\n const { buttonId, targetKey, updateAttachable, isReqStateInProgress } = props\n\n const onClick = () => updateAttachable(buttonId, targetKey)\n\n return (\n \n
\n \n {I18n.t('project_tickets.titles.shared_uploads')}\n \n
\n
\n \n \n )\n}\n\nconst isReqStateInProgress = createLoadingSelector([types.UPDATE_ATTACHABLE])\n\nexport default connect((state, props) => {\n const uploaderData = state.getIn(['uploader', props.buttonId])?.toJS() || {}\n return ({\n isReqStateInProgress: isReqStateInProgress(state) || uploaderData.state == 'uploading',\n ...(state.getIn(['uploader', props.buttonId])?.toJS() || {})\n })\n}, { updateAttachable })(SharedUploads)\n","import React, { useEffect } from 'react'\nimport { initUploader } from 'actions/uploader_actions'\nimport store from 'store'\nimport { Provider } from 'react-redux'\n\nimport ProjectUploads from './project_uploads'\nimport SharedUploads from './shared_uploads'\n\nconst MspSharedUploadsApp = ({ projectUploader, mspProjectUploader }) => {\n\n store.dispatch(initUploader(projectUploader.buttonId, projectUploader))\n store.dispatch(initUploader(mspProjectUploader.buttonId, mspProjectUploader))\n\n return (\n \n \n \n )\n}\n\nexport default MspSharedUploadsApp\n","import * as React from 'react'\nimport { useEffect, useState } from 'react'\nimport { Button, Form, Input, Label } from 'reactstrap'\nimport { isUndefined } from 'lodash'\nimport axios, { Method, ResponseType } from 'axios'\nimport Select from '../../shared/base_react_select'\nimport Spinner from '../../shared/spinner'\n\nconst { I18n, $ } = window as any\n\ninterface DuplicateFormProps {\n projectId: number\n projectGroupId: number\n projectGroups: any\n}\n\nfunction loadLocations(callback, setFetching) {\n const ajaxParams = {\n method: 'get' as Method,\n responseType: 'json' as ResponseType,\n }\n\n setFetching(true)\n axios('/locations.json', ajaxParams)\n .then((response) => {\n const { data } = response\n if (data) {\n callback(data)\n } else {\n callback([])\n }\n setFetching(false)\n })\n .catch(() => {\n setFetching(false)\n })\n}\n\nfunction DuplicateForm({\n projectId,\n projectGroupId,\n projectGroups,\n}: DuplicateFormProps) {\n const [isFetching, setIsFetching] = useState(false)\n const [locations, setLocations] = useState([])\n\n useEffect(() => {\n loadLocations(setLocations, setIsFetching)\n }, [])\n\n const [selectAll, setSelectAll] = useState(false)\n const [selectedLocations, setSelectedLocations] = useState({})\n\n if (!locations) {\n return null\n }\n const token = $('meta[name=csrf-token]').attr('content')\n const projectGroupsOptions = projectGroups.map((g) => {\n return {\n value: g[1],\n label: g[0],\n }\n })\n\n const isLocationSelected = (locationId) =>\n isUndefined(selectedLocations[locationId]) ? selectAll : selectedLocations[locationId]\n\n return (\n \n )\n}\n\nexport default DuplicateForm\n","\nimport * as React from 'react'\nimport { Modal, ModalBody, ModalHeader } from 'reactstrap'\nimport DuplicateForm from './duplicate_form'\n\ninterface ModalWindowProps {\n projectId: number\n isOpen: boolean\n toggle: () => void\n toggleModal: () => void\n windowTitle: string\n projectGroupId: number\n projectGroups: any\n}\n\nfunction ModalWindow({\n projectId,\n isOpen,\n toggle,\n toggleModal,\n windowTitle,\n projectGroupId,\n projectGroups,\n}: ModalWindowProps) {\n return (\n \n \n {windowTitle}\n \n \n \n \n \n )\n}\n\nexport default ModalWindow\n","import React, { useState } from 'react'\nimport store from 'store'\nimport { Provider } from 'react-redux'\nimport { Button } from 'reactstrap'\nimport ModalWindow from './modal_window'\n\nfunction DuplicateButtonApp({ buttonTitle, windowTitle, projectId, projectGroupId, projectGroups }) {\n const [modalFormIsActive, setModalFormIsActive] = useState(false)\n const toggleModal = () => setModalFormIsActive(!modalFormIsActive)\n\n return (\n \n \n \n \n
\n \n )\n}\n\nexport default DuplicateButtonApp\n","import * as React from 'react'\n\ntype SelectedLocationsInfoProps = {\n selectedProjects: number[]\n availableProjects: any[]\n}\n\nconst { I18n } = window as any\n\nexport default function SelectedLocationsInfo(props: SelectedLocationsInfoProps) {\n const { selectedProjects, availableProjects } = props\n\n const projectsCountHint = I18n.t('project_groups.send_links.projects_count_hint').replace(\n '%{amount}',\n selectedProjects.length\n )\n const selectedLocationNames = selectedProjects.map((projectId) => {\n if (!availableProjects) {\n return null\n }\n const project = availableProjects.find((p) => p.get('id') === projectId)\n return project.get('location_name')\n })\n return (\n \n
\n \n {`${projectsCountHint}:`}\n
\n
\n {selectedLocationNames.map((locationName) => (\n - {locationName}
\n ))}\n
\n
\n )\n}\n","import * as React from 'react'\nimport { Button, Form, Input, Label } from 'reactstrap'\nimport SelectedLocationsInfo from './selected_locations_info'\n\nconst { I18n, $ } = window as any\n\ntype SendLinksFormProps = {\n actionUrl: string\n toggleModal: () => void\n selectedProjects: number[]\n setCurrentStep: (step: number) => void\n availableProjects: any[]\n}\n\nfunction SendLinksForm(props: SendLinksFormProps) {\n const { actionUrl, toggleModal, selectedProjects, setCurrentStep, availableProjects } = props\n const token = $('meta[name=csrf-token]').attr('content')\n\n const defaultSubject = I18n.t('project_groups.send_links.default_subject')\n const defaultMessage = I18n.t('project_groups.send_links.default_message')\n\n return (\n \n )\n}\n\nexport default SendLinksForm\n","import React from 'react'\n\nconst { I18n } = window\n\nfunction SelectProjects({ availableProjects, selectedProjects, setSelectedProjects }) {\n const defaultSelected = availableProjects.map((p) => p.get('id')).toJS()\n const allSelected = selectedProjects.sort().toString() === defaultSelected.sort().toString()\n const toggleAll = () => {\n if (allSelected) {\n setSelectedProjects([])\n } else {\n setSelectedProjects(defaultSelected)\n }\n }\n const toggleOne = (id) => {\n if (selectedProjects.includes(id)) {\n setSelectedProjects(selectedProjects.filter((e) => e !== id))\n } else {\n setSelectedProjects([...selectedProjects, id])\n }\n }\n\n return (\n \n )\n}\n\nexport default SelectProjects\n","import { connect } from 'react-redux'\nimport { loadProjects, types } from 'actions/projects/send_links_button_actions'\nimport { createLoadingSelector } from 'libs/requests_status_handlers'\n\nconst loadingSelector = createLoadingSelector([types.LOAD_PROJECTS])\n\nexport default connect(\n (state) => ({\n isFetching: loadingSelector(state),\n availableProjects: state.getIn(['projects', 'send_links', 'availableProjects']),\n }),\n (dispatch, ownProps) => ({\n loadProjects: (ids) => dispatch(loadProjects(ownProps.projectsUrl, ids)),\n })\n)\n","import * as React from 'react'\nimport { useState, useEffect } from 'react'\nimport { Button, Modal, ModalBody, ModalFooter, ModalHeader } from 'reactstrap'\nimport SpinnerOverlay from '../../shared/spinner_overlay'\nimport SendLinksForm from './send_links_form'\nimport SelectProjects from './select_projects'\nimport connect from './connect'\n\nconst { I18n, $ } = window as any\n\ntype ModalWindowProps = {\n isOpen: boolean\n toggle: () => void\n toggleModal: () => void\n windowTitle: string\n // projectGroupId: number\n actionUrl: string\n loadProjects: (projectIds: number[]) => void\n isFetching: boolean\n availableProjects: any[]\n}\n\nfunction ModalWindow(props: ModalWindowProps) {\n const {\n isOpen,\n toggle,\n toggleModal,\n windowTitle,\n // projectGroupId,\n actionUrl,\n loadProjects,\n isFetching,\n availableProjects,\n } = props\n const [currentStep, setCurrentStep] = useState(0)\n const availableProjectIds = $('.projects-table tbody tr')\n .map(function () {\n return parseInt(this.id.replace(/^project_/, ''), 10)\n })\n .get()\n useEffect(() => {\n loadProjects(availableProjectIds)\n }, [])\n const [selectedProjects, setSelectedProjects] = useState(availableProjectIds)\n return (\n \n {windowTitle}\n \n {currentStep === 0 && availableProjects && (\n \n \n \n )}\n {currentStep === 1 && (\n \n )}\n \n {currentStep === 0 && (\n \n \n \n \n )}\n \n )\n}\n\nexport default connect(ModalWindow)\n","import React, { useState } from 'react'\nimport PropTypes from 'prop-types'\nimport store from 'store'\nimport { Provider } from 'react-redux'\nimport { Button } from 'reactstrap'\nimport ModalWindow from './modal_window'\n\nfunction SendLinksButtonApp({\n buttonTitle,\n windowTitle,\n projectsUrl,\n actionUrl,\n projectGroupId,\n}) {\n const [modalFormIsActive, setModalFormIsActive] = useState(false)\n const toggleModal = () => setModalFormIsActive(!modalFormIsActive)\n\n return (\n \n \n {modalFormIsActive && }\n \n )\n}\n\nSendLinksButtonApp.propTypes = {\n buttonTitle: PropTypes.string.isRequired,\n windowTitle: PropTypes.string.isRequired,\n actionUrl: PropTypes.string.isRequired,\n projectGroupId: PropTypes.number.isRequired,\n projectsUrl: PropTypes.string.isRequired,\n}\n\nexport default SendLinksButtonApp\n","import { useState } from 'react'\nimport Spinner from 'components/shared/spinner'\nimport ajax from 'axios'\nimport {\n Button, Modal, ModalHeader, ModalBody, ModalFooter,\n Form, FormGroup, Label, Input, FormText\n} from 'reactstrap'\n\nconst { I18n } = window\n\nconst AssignHandoverFormButton = ({ projectId, handoverForms, formId }) => {\n\n const [isOpen, setOpenFlag] = useState(false)\n const [selectedFormId, setFormId] = useState(formId)\n const [isReqInProgress, setProcessingStateOfReq] = useState(false)\n\n const changeFormId = (e) => setFormId(e.target.value)\n\n const assignHandoverForm = () => {\n setProcessingStateOfReq(true)\n ajax({\n url: `/project_tickets/${projectId}/assign-handover-form.json`,\n method: 'patch',\n data: { form_id: selectedFormId }\n }).then(resp => {\n setOpenFlag(false)\n setProcessingStateOfReq(false)\n window.location.href = resp.data.redirect_to\n })\n }\n\n const toggle = () => setOpenFlag(prevVal => !prevVal)\n\n return (\n \n
\n
\n \n {I18n.t('project_tickets.titles.assign_handover_form_modal_window')}\n {isReqInProgress && }\n \n \n \n \n \n
\n )\n}\n\nAssignHandoverFormButton.defaultProps = {\n handoverForms: [],\n formId: ''\n}\n\nexport default AssignHandoverFormButton\n","import React, { useState } from 'react'\nimport PropTypes from 'prop-types'\nimport Select from 'components/shared/base_react_select'\n\nconst { I18n } = window\n\nconst RecipientList = ({ recipients, onChangeClick }) => (\n <>\n {`${I18n.t('comments.will_be_emailed_to')}:`}\n
\n \n {recipients.map(r => r.label).join(', ')}\n \n { recipients.map(r => (\n \n )) }\n \n >\n)\n\nconst RecipientSelect = ({ selectedRecipients, availableRecipients }) => (\n <>\n { `${I18n.t('comments.email_to_people')}:` }\n \n >\n)\n\nconst CommentRecipients = ({ recipients, users }) => {\n const [editing, setEditing] = useState(false)\n\n return (\n \n { !editing && setEditing(true)} /> }\n { editing && }\n
\n )\n}\n\nCommentRecipients.propTypes = {\n recipients: PropTypes.array.isRequired,\n users: PropTypes.array.isRequired,\n}\nexport default CommentRecipients\n","import * as React from 'react'\nimport { useState } from 'react'\nimport DateTimeInput from '../../shared/date_time_input'\n\nconst { I18n } = window as any\ntype RescheduleButtonProps = {\n date: string\n authenticityToken: string\n timeZone: string\n}\n\nexport default function RescheduleButtonApp(props: RescheduleButtonProps) {\n const { date, authenticityToken, timeZone } = props\n const [showForm, setShowForm] = useState(false)\n const label = date ? I18n.t('project_tickets.actions.reschedule') : I18n.t('project_tickets.actions.schedule')\n return (\n \n {date &&
{date}}\n {!showForm && (\n
\n )}\n {showForm && (\n
\n )}\n
\n )\n}\n","import * as React from 'react'\nimport axios, { Method, ResponseType } from 'axios'\nimport Select, { Async } from '../../shared/base_react_select'\n\ntype CompanyShape = {\n id: number\n name: string\n}\n\ninterface SelectProjectProps {\n companies: CompanyShape[]\n projectsIndexUrl: string\n selectedCompany: {\n id: number\n name: string\n }\n selectedProject: {\n id: number\n companyId: number\n location_name: string\n title: string\n }\n}\n\nfunction loadProjectOptions(url, filter, callback) {\n const ajaxParams = {\n method: 'get' as Method,\n responseType: 'json' as ResponseType,\n params: { ...filter },\n }\n axios(url, ajaxParams).then((response) => {\n const { data } = response\n if (data) {\n const options = data.map((project) => {\n return { label: `${project.id} - ${project.location_name} - ${project.title}`, value: project.id, project }\n })\n callback(options)\n } else {\n callback([])\n }\n })\n}\n\nexport default function SelectProject(props: SelectProjectProps) {\n const { companies, selectedProject, selectedCompany, projectsIndexUrl } = props\n const companyOptions = companies.map((c) => ({ value: c.id, label: c.name }))\n const selectedCompanyOption = selectedCompany ? companyOptions.find((c) => c.value === selectedCompany.id) : null\n const defaultCompany = selectedProject ? companyOptions.find((c) => c.value === selectedProject.companyId) : selectedCompanyOption\n const [company, setCompany] = React.useState(defaultCompany)\n const defaultProject = selectedProject\n ? { value: selectedProject.id, label: `${selectedProject.id} - ${selectedProject.location_name} - ${selectedProject.title}` }\n : null\n const [project, setProject] = React.useState(defaultProject)\n const { I18n } = window as any\n return (\n \n )\n}\n","import * as React from 'react'\nimport { Button } from 'reactstrap'\nimport InstallersSelectorApp from '../../../shared/project_form_controls/installers_selector_app'\nimport { VCSpinner } from '../../../shared/spinner'\n\nconst { I18n } = window as any\n\ntype Option = {\n value: number\n label: string\n}\n\ninterface SelectProjectTicketProps {\n collection: Option[]\n value: number[]\n canCreateInstaller: boolean\n multi: boolean\n assignInstallers: (projectTicketIds: number[], installerIds: number[]) => void\n ready_to_reload_page: boolean\n}\n\nexport default function SelectProjectTicket(props: SelectProjectTicketProps) {\n const { collection, value, canCreateInstaller, multi, assignInstallers, ready_to_reload_page } = props\n const [isSelectMode, setIsSelectMode] = React.useState(false)\n const [selectedProjectTickets, setSelectedProjectTickets] = React.useState([])\n const [selectedInstallers, setSelectedInstallers] = React.useState