Skip to content

Instantly share code, notes, and snippets.

@renderlife
Created April 9, 2021 12:19
Show Gist options
  • Save renderlife/58aa5ff93cb80bdc41457ded822d8df8 to your computer and use it in GitHub Desktop.
Save renderlife/58aa5ff93cb80bdc41457ded822d8df8 to your computer and use it in GitHub Desktop.
2246
import React, { Component, useCallback } from 'react'
import {
Form,
FormGroup,
Button,
Nav,
NavItem,
NavLink,
TabContent,
TabPane,
DropdownItem,
Popover,
PopoverBody
} from 'reactstrap'
import { bindActionCreators } from 'redux'
import { connect } from 'react-redux'
import * as CompanyActions from '../../actions/company'
import * as NetworksActions from '../../actions/networks'
import * as UploadActions from '../../actions/upload'
import * as TasksActions from '../../actions/tasks'
import * as RubricsActions from '../../actions/rubrics'
import * as FieldsActions from '../../actions/fields'
import * as FeatureElementsActions from '../../actions/featureElements'
import * as GoogleCategoriesActions from '../../actions/googleCategories'
import * as CountriesActions from '../../actions/countries'
import * as ProvidersActions from '../../actions/providers'
import * as CompanyStatusesActions from '../../actions/companyStatuses'
import * as CompanyImportExceptionsActions from '../../actions/companyImportExceptions'
import TasksFinisher from '../HOC/TasksFinisher'
import { Link, withRouter } from 'react-router-dom'
import OptimizedSelect from '../common/Selects/OptimizedSelect'
import API from '../../services/API'
import classnames from 'classnames'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import {
YANDEX_PROVIDER_ID,
GOOGLE_PROVIDER_ID,
TWOGIS_PROVIDER_ID,
LANG_RU,
LANG_EN,
GRUZINSKIE_KANIKULI_PROVIDER_ID,
OTZOVIK_PROVIDER_ID,
IRECOMMEND_PROVIDER_ID,
COMPANY_STATUS_CLOSED,
PHOTO_UPDATE_MODES,
PHONE_UPDATE_MODES,
LANGUAGES,
PHONE_TYPES,
PHOTO_TYPES,
FEATURE_BOOLEAN_ID,
FEATURE_ENUM_SINGLE_ID,
FEATURE_ENUM_MULTIPLE_ID,
GOOGLE_ATTRIBUTE_TYPES,
SELECT_BOOLEAN_VALUES,
TYPE_WORKING_TIME,
TYPE_BREAK_TIME,
TYPE_ADDRESS,
TOAST_TYPES,
BAD_REQUEST_TOAST_MESSAGE,
WORKING_TIME_IDS,
ORGMOD_VALUE,
GOOGLE_ATTRIBUTE_BOOLEAN_ID,
GOOGLE_ATTRIBUTE_TEXT_SINGLE_ID,
GOOGLE_ATTRIBUTE_ENUM_SINGLE_ID,
GOOGLE_ATTRIBUTE_BOOLEAN_MULTIPLE_ID,
} from '../../constants/constants'
import { LONGITUDE_ID, LATITUDE_ID } from '../../constants/fieldNameIds'
import gmbErrorsTranslations from '../../constants/gmbErrorsTranslations'
import { formatDateTime } from '../../helpers/time'
import BusyNetworkAlert from '../common/modals/BusyNetworkAlert'
import arraySort from 'array-sort'
import { arrayMove } from 'react-sortable-hoc'
import { formatGoogleAttributeLabel } from '../../constants/utils'
import { createToast } from '../../helpers/toast'
import { getMessageNotValidPhoneNumber } from '../../helpers/phone'
import TooltipComponent from '../common/TooltipComponent'
import { goBack } from 'connected-react-router'
import TabMenu from '../common/TabMenu'
import { getCompanyChangelogsMenu } from '../../constants/tabMenus'
import CheckResult, { TYPE_COMPANY_CHECK_ERRORS } from '../common/CheckResult'
import FieldsBlock from './Blocks/FieldsBlock'
import { PhotoSortableList } from './Blocks/Photos'
import { PhoneSortableList } from './Blocks/Phones'
import FeaturesBlock from './Blocks/FeaturesBlock'
import { CONFIG } from '../../constants/config'
import Loader from '../common/Loader'
import RebaseCompanyModal from '../common/modals/RebaseCompanyModal'
import Input, { Label } from '../common/InputComponents'
import Notifications from '../common/Notifications'
import Analytics, { EVENT_BRANCH_UPDATE, EVENT_BRANCH_MULTIPLE_UPDATE } from '../../services/Analytics'
import FormGroupTextInput from '../common/inputs/FormGroupTextInput'
import FormGroupCheckbox from '../common/inputs/FormGroupCheckbox'
import { ImportIgnoredButton } from './Blocks/IgnoredButton'
import ActionsDropdown from '../common/ActionsDropdown'
import { getEnabledProviders } from '../../helpers/provider'
const PHOTO_TYPE_COMPANY_PHOTO = 'companyPhoto'
const PHOTO_TYPE_COMPANY_LOGO = 'companyLogo'
const PHOTOS_NAME_ID = 50
const PHONES_NAME_ID = 51
const FEATURES_NAME_ID = 52
const RUBRICS_NAME_ID = 53
const STATUS_NAME_ID = 54
const DISPLAY_HEADER_ADDRESS_IDS = [105, 4, 18]
const INVALID_FEEDBACK_STYLE = { display: 'block' }
const TOOLTIP_DESCRIPTION_STYLE = { marginLeft: 4 }
function SelectGoogleAttributeYesNo(props) {
const { currentValue, onChange, disabled, attribute } = props
const handleChange = useCallback(value => onChange(value ? value.value : '', attribute), [onChange, attribute])
return (
<OptimizedSelect
options={SELECT_BOOLEAN_VALUES}
onChange={handleChange}
value={currentValue}
isDisabled={disabled}
isClearable={true}
/>
)
}
function InputGoogleAttributeText(props) {
const { currentValue, onChange, disabled, attribute } = props
const handleChange = useCallback(e => onChange(e.target.value, attribute), [onChange, attribute])
return <Input placeholder="Значение" onChange={handleChange} value={currentValue} disabled={disabled} />
}
function SelectGoogleAttributeEnum(props) {
const { options, currentValue, disabled, attribute, onChange, multieditValue } = props
const handleChange = useCallback(value => onChange(attribute.name, value, multieditValue), [onChange, attribute, multieditValue])
return (
<OptimizedSelect
options={options}
onChange={handleChange}
value={currentValue}
isDisabled={disabled}
isClearable={true}
/>
)
}
function SelectGoogleAttributeReplace(props) {
const { currentValue, attribute, onChange } = props
const handleChange = useCallback(value => onChange(attribute.id, value), [onChange, attribute])
return (
<OptimizedSelect
key="replace"
options={GOOGLE_ATTRIBUTES_MULTIEDIT_OPTIONS}
className="ml-3 replace-select"
placeholder="Оставить как есть"
value={currentValue}
onChange={handleChange}
isClearable={true}
/>
)
}
function ProviderDisabledButton(props) {
const { color, provider, onClick } = props
const handleClick = useCallback(() => onClick(provider), [onClick, provider])
return (
<Button color={color} className="mr-2 mb-2" onClick={handleClick}>
{provider.label}
</Button>
)
}
function NavLang(props) {
const { languageCode, active, label, onClick } = props
const handleClick = useCallback(() => {
onClick(languageCode)
}, [languageCode, onClick])
return (
<NavItem>
<NavLink
className={classnames({
active: active === languageCode
})}
onClick={handleClick}
>
{label}
{(languageCode === LANG_RU || languageCode === LANG_EN) && (
<TooltipComponent text="Заполнение языков учитывается в заполненности компании" />
)}
</NavLink>
</NavItem>
)
}
const InputImage = React.forwardRef((props, ref) => {
const { type, name, onChange } = props
const handleChange = useCallback(e => onChange(e, type, name), [type, name, onChange])
return <input className="logo-input" type="file" onChange={handleChange} ref={ref} />
})
export const IMPORT_EXCEPTION_TYPES_MULTIEDIT_OPTIONS = [
{ value: 'importExceptionTypeIdsForReplace', label: 'Заменить' },
{ value: 'importExceptionTypeIdsForDelete', label: 'Удалить' }
]
export const FIELDS_MULTIEDIT_OPTIONS = [
{ value: 'fieldsForAppend', label: 'Добавить' },
{ value: 'fieldsForReplace', label: 'Заменить' },
{ value: 'fieldsForDelete', label: 'Очистить' }
]
export const FEATURES_MULTIEDIT_OPTIONS = [
{ value: 'featuresForAppend', label: 'Добавить' },
{ value: 'featuresForReplace', label: 'Заменить' },
{ value: 'featuresForDelete', label: 'Очистить' }
]
export const GOOGLE_ATTRIBUTES_MULTIEDIT_OPTIONS = [
{ value: 'googleAttributesForReplace', label: 'Заменить' },
{ value: 'googleAttributesForDelete', label: 'Удалить' }
]
class CompanyForm extends Component {
constructor(props) {
super(props)
this.state = {
isLoading: true,
notFound: false,
networkId: null,
id: undefined,
name: '',
rubricIds: [],
fields: [],
phones: [],
photos: [],
uploaded: [],
features: [],
rubricFeatures: [],
rubricFeaturesOptions: [],
googleCategoryAttributes: [],
languageCodeIds: [],
nameLanguageCodeId: null,
activeLanguageTab: 1,
submitDisabled: false,
comment: '',
yandexMapsLink: '',
googleMapsLink: '',
twogisMapsLink: '',
googleCategories: [],
googleLabels: '',
googleAttributes: [],
googleId: '',
yandexMapsId: '',
gmbLocationId: '',
twogisId: '',
statusTypeId: 1,
importExceptionTypeIds: [],
division: '',
yandexSpravId: '',
yandexMarketXmlImportLink: '',
hasYandexMarketXmlImportLink: false,
isGoogleCheckDisabled: undefined,
isYandexXmlFeedEnabled: undefined,
isXmlImportEnabled: undefined,
hasCityOnTwogisMaps: true,
isDeletedOnYandexMaps: false,
isDeletedOnTwogis: false,
tripadvisorId: '',
importExceptionTypeIdsForReplace: [],
importExceptionTypeIdsForDelete: [],
featuresForAppend: [],
featuresForReplace: [],
featuresForDelete: [],
googleAttributesForReplace: [],
googleAttributesForDelete: [],
rubricsForReplace: [],
rubricsForDelete: [],
photosForAppend: [],
photosForReplace: [],
phonesForAppend: [],
phonesForReplace: [],
createdAt: null,
zoonId: '',
zoonLink: '',
yellId: '',
yellLink: '',
foursquareId: '',
foursquareLink: '',
facebookLocationId: '',
facebookStoreNumber: '',
postingDisabledProviderIds: [],
yandexId: null,
internalYandexXmlId: null,
logoUploadId: null,
logoUrl: null,
logoOriginalUrl: null,
logoPopoverOpen: false,
photosUpdateMode: PHOTO_UPDATE_MODES[0],
phonesUpdateMode: PHONE_UPDATE_MODES[0],
enabledProviders: {}
}
this.rebaseCompanyModalRef = null
this.logoInput = React.createRef()
}
async componentDidMount() {
const {
match,
networks,
rubrics,
features,
googleCategoriesOptions,
countryCodes,
providers,
companyStatuses,
companyImportExceptionsOptions
} = this.props
const isMultifill = this.isMultifill()
if (networks.length === 0) {
await this.props.doNetworksLoad()
}
const network = networks
? this.props.networks.find(item => Number(item.id) === Number(match.params.networkId))
: null
if (rubrics.length === 0) {
await this.props.doRubricsLoad()
}
if (this.props.fields.length === 0) {
await this.props.doFieldsLoad()
}
if (features.length === 0) {
this.props.doFeatureElementsLoad()
}
if (googleCategoriesOptions.length === 0) {
this.props.doGoogleCategoriesLoad()
}
if (countryCodes.length === 0) {
this.props.doCountriesLoad()
}
if (providers.length === 0) {
this.props.doProvidersLoad()
}
if (companyStatuses.length === 0) {
this.props.doCompanyStatusesLoad()
}
if (companyImportExceptionsOptions.length === 0) {
this.props.doCompanyImportExceptionsLoad()
}
if (match?.params?.companyId && !isMultifill) {
this.props.doLoadCompany(match.params.companyId)
} else {
const languageCodeIds = network?.languages ? network.languages.map(item => item.language.codeId) : []
const fields = []
let rubricFeatures = []
this.props.fields
.filter(item => !item.isHiddenInCompany && !item.isMultilanguage)
.forEach(field => {
const sortIndex = field.positionIndex
fields.push({
nameId: field.id,
value: '',
languageCodeId: null,
sortIndex,
action: isMultifill ? null : undefined
})
})
this.props.fields
.filter(item => !item.isHiddenInCompany && item.isMultilanguage)
.forEach(field => {
const sortIndex = field.positionIndex
languageCodeIds.forEach(languageCodeId => {
fields.push({
nameId: field.id,
value: '',
languageCodeId,
sortIndex,
action: isMultifill ? null : undefined
})
})
})
fields.forEach((item, index) => {
item.index = index
})
if (network?.rubric) {
const featuresResult = await API.request(`features?rubricIds=[${network.rubric.id}]&limit=all`)
if (featuresResult?.features) {
rubricFeatures = featuresResult?.features || []
}
}
this.setState(state => ({
isLoading: false,
networkId: Number(match.params.networkId),
fields: arraySort(fields, 'sortIndex'),
languageCodeIds,
activeLanguageTab: languageCodeIds.length > 0 ? languageCodeIds[0] : 1,
nameLanguageCodeId: languageCodeIds.length > 0 ? languageCodeIds[0] : null,
statusTypeId: this.isMultifill() ? null : state.statusTypeId,
rubricFeatures,
rubricFeaturesOptions: this.makeRubricFeaturesOptions(rubricFeatures),
isXmlImportEnabled: this.isMultifill(),
enabledProviders: getEnabledProviders(network)
}))
}
const googleCategoryAttributes = await API.request(
`google/attributes?categoryIds=[${
network ? network.googleCategoryId : undefined
}]&isDeprecated=false&limit=all`
)
this.setState({
googleCategoryAttributes: googleCategoryAttributes?.attributes || []
})
}
async UNSAFE_componentWillReceiveProps(nextProps) {
const { match, history, me } = nextProps
if (nextProps.action === CompanyActions.COMPANY_LOAD_FAILURE) {
this.setState({
notFound: true,
isLoading: false
})
}
if (nextProps.action === CompanyActions.COMPANY_LOAD_SUCCESS) {
if (!nextProps.loaded) {
return
}
if (Number(match.params.networkId) !== nextProps.loaded.network.id) {
this.setState({
notFound: true,
isLoading: false
})
return
}
const network = nextProps.networks
? nextProps.networks.find(item => Number(item.id) === Number(match.params.networkId))
: null
const languageCodeIds = network?.languages ? network.languages.map(item => item.language.codeId) : []
const rubrics = [...nextProps.loaded.rubrics.map(item => item.id), nextProps.loaded.network.rubricId]
const resultFields = await API.request(`features?rubricIds=[${rubrics.join(',')}]&limit=all`)
const resultFieldsFeatures = resultFields?.features || []
const features = []
nextProps.loaded.features.forEach(item => {
features.push({
id: item.id,
featureId: item.featureId,
nameId: item.element.nameId,
name: item.viewAttrs.hasOwnProperty('name') ? item.viewAttrs.name : null,
excludedFromYandexCheck: item.excludedFromYandexCheck,
...item.inputAttrs
})
})
const state = {
isLoading: false,
networkId: nextProps.loaded.network.id,
id: nextProps.loaded.id,
name: nextProps.loaded.name,
rubricIds: nextProps.loaded.rubrics.map(item => item.id),
uploaded: [...nextProps.loaded.photos],
features,
rubricFeatures: resultFieldsFeatures,
rubricFeaturesOptions: this.makeRubricFeaturesOptions(resultFieldsFeatures),
comment: nextProps.loaded.comment,
yandexMapsLink: nextProps.loaded.yandexMapsLink,
googleMapsLink: nextProps.loaded.googleMapsLink,
twogisMapsLink: nextProps.loaded.twogisMapsLink,
languageCodeIds,
activeLanguageTab: languageCodeIds.length > 0 ? languageCodeIds[0] : 1,
nameLanguageCodeId: languageCodeIds.length > 0 ? languageCodeIds[0] : null,
fields: [],
googleCategories: nextProps.loaded.googleCategories.map(item => {
return { value: item.id, label: item.nameRu }
}),
googleLabels: nextProps.loaded.googleLabels.map(item => item.value).join('\n'),
googleAttributes: nextProps.loaded.googleAttributes.map(item => {
return { attributeId: item.attributeId, value: item.value ? item.value : undefined }
}),
googleId: nextProps.loaded.googleId ? nextProps.loaded.googleId : '',
yandexMapsId: nextProps.loaded.yandexMapsId ? nextProps.loaded.yandexMapsId : '',
gmbLocationId: nextProps.loaded.gmbLocationId ? nextProps.loaded.gmbLocationId : '',
twogisId: nextProps.loaded.twogisId ? nextProps.loaded.twogisId : '',
tripadvisorId: nextProps.loaded.tripadvisorId ? nextProps.loaded.tripadvisorId : '',
statusTypeId: nextProps.loaded.statusTypeId ? nextProps.loaded.statusTypeId : 1,
importExceptionTypeIds: nextProps.loaded.importExceptions.map(item => item.typeId),
division: nextProps.loaded.division ? nextProps.loaded.division : '',
yandexSpravId: nextProps.loaded.yandexSpravId ? nextProps.loaded.yandexSpravId : '',
yandexMarketXmlImportLink: nextProps.loaded.yandexMarketXmlImportLink
? nextProps.loaded.yandexMarketXmlImportLink
: '',
hasYandexMarketXmlImportLink: Boolean(nextProps.loaded.yandexMarketXmlImportLink),
isGoogleCheckDisabled: nextProps.loaded.isGoogleCheckDisabled,
isYandexXmlFeedEnabled: nextProps.loaded.isYandexXmlFeedEnabled,
isXmlImportEnabled: nextProps.loaded.isXmlImportEnabled,
hasCityOnTwogisMaps: nextProps.loaded.hasCityOnTwogisMaps,
isDeletedOnYandexMaps: nextProps.loaded.isDeletedOnYandexMaps,
isDeletedOnTwogis: nextProps.loaded.isDeletedOnTwogis,
createdAt: nextProps.loaded.createdAt,
zoonId: nextProps.loaded.zoonId,
zoonLink: nextProps.loaded.zoonLink,
yellId: nextProps.loaded.yellId,
yellLink: nextProps.loaded.yellLink,
foursquareId: nextProps.loaded.foursquareId,
foursquareLink: nextProps.loaded.foursquareLink,
facebookLocationId: nextProps.loaded.facebookLocationId,
facebookStoreNumber: nextProps.loaded.facebookStoreNumber,
postingDisabledProviderIds: nextProps.loaded.postingDisabledProviders.map(item => item.id),
yandexId: nextProps.loaded.yandexId,
internalYandexXmlId: nextProps.loaded.internalYandexXmlId,
logoUrl: nextProps.loaded.logoUpload ? nextProps.loaded.logoUpload.image.smallUrl : null,
logoOriginalUrl: nextProps.loaded.logoUpload ? nextProps.loaded.logoUpload.image.originalUrl : null,
enabledProviders: getEnabledProviders(network)
}
const loadedFields = nextProps.loaded.fields.map(item => ({
id: item.id,
nameId: item.nameId,
value: item.value,
languageCodeId:
item.hasOwnProperty('attrs') &&
item.attrs &&
item.attrs.hasOwnProperty('language') &&
item.attrs.language
? item.attrs.language.codeId
: null,
providerIds: item.providerIds
}))
nextProps.fields
.filter(item => !item.isHiddenInCompany && !item.isMultilanguage)
.forEach(field => {
const sortIndex = field.positionIndex
const fields = loadedFields.filter(item => item.nameId === field.id)
if (fields.length > 0) {
fields.forEach(field => {
const item = { ...field, sortIndex }
state.fields.push(item)
})
} else {
state.fields.push({
nameId: field.id,
value: '',
languageCodeId: null,
sortIndex
})
}
})
nextProps.fields
.filter(item => !item.isHiddenInCompany && item.isMultilanguage)
.forEach(field => {
const sortIndex = field.positionIndex
languageCodeIds.forEach(languageCodeId => {
const fields = loadedFields.filter(
item => item.nameId === field.id && item.languageCodeId === languageCodeId
)
if (fields.length > 0) {
fields.forEach(field => {
const item = { ...field, sortIndex }
state.fields.push(item)
})
} else {
state.fields.push({
nameId: field.id,
value: '',
languageCodeId,
sortIndex
})
}
})
})
state.fields = arraySort(state.fields, 'sortIndex')
state.fields.forEach((item, index) => {
item.index = index
})
state.phones = nextProps.loaded.phones.map(item => ({
id: item.id,
typeId: item.typeId,
info: item.info,
number: item.number,
countryNumericCode: item.country ? item.country.numericCode : null,
ext: item.ext,
providerIds: item.providerIds,
notValidMessage: getMessageNotValidPhoneNumber(
item.country ? item.country.phoneCode : null,
item.number
)
}))
state.photos = nextProps.loaded.photos.map(item => ({
id: item.id,
alt: item.alt,
typeId: item.typeId,
tagId: item.tagId
}))
this.setState(state)
}
if (
nextProps.action === CompanyActions.COMPANY_CREATE_SUCCESS ||
nextProps.action === CompanyActions.COMPANY_UPDATE_SUCCESS ||
nextProps.action === CompanyActions.COMPANY_MULTIPLE_UPDATE_SUCCESS
) {
nextProps.doCompanyClearErrs()
history.push(`/networks/${match.params.networkId}`, {})
}
if (
nextProps.action === CompanyActions.COMPANY_CREATE_SUCCESS ||
nextProps.action === CompanyActions.COMPANY_CREATE_FAILURE ||
nextProps.action === CompanyActions.COMPANY_UPDATE_SUCCESS ||
nextProps.action === CompanyActions.COMPANY_UPDATE_FAILURE
) {
this.setState({ submitDisabled: false })
}
if (
nextProps.action === CompanyActions.COMPANY_CREATE_FAILURE ||
nextProps.action === CompanyActions.COMPANY_UPDATE_FAILURE
) {
if (nextProps.err && nextProps.err.type === 'BadRequestError') {
createToast(BAD_REQUEST_TOAST_MESSAGE, { type: TOAST_TYPES.ERROR })
}
}
if (nextProps.action === CompanyActions.COMPANY_UPDATE_SUCCESS) {
Analytics.sendEvent(EVENT_BRANCH_UPDATE, {
user_id: me.id,
is_admin: Number(me.isAdmin),
networks: [Number(match.params.networkId)],
companies: [Number(match.params.companyId)]
})
}
if (nextProps.action === CompanyActions.COMPANY_MULTIPLE_UPDATE_SUCCESS) {
if (match.params.companyIds === 'all') {
Analytics.sendEvent(EVENT_BRANCH_MULTIPLE_UPDATE, {
user_id: me.id,
is_admin: Number(me.isAdmin),
networks: [Number(match.params.networkId)]
})
} else {
Analytics.sendEvent(EVENT_BRANCH_MULTIPLE_UPDATE, {
user_id: me.id,
is_admin: Number(me.isAdmin),
networks: [Number(match.params.networkId)],
companies: match.params.companyIds.split(',').map(item => Number(item))
})
}
}
}
componentWillUnmount() {
this.props.doCompanyClearErrs()
}
isMultifill = () => {
const { match } = this.props
return match.path === '/networks/:networkId/multi-fill/:companyIds'
}
makeRubricFeaturesOptions = rubricFeatures => {
return rubricFeatures.map(item => ({
value: item.id,
label: item.valueRu ? item.valueRu : item.viewAttrs.value,
name: item.viewAttrs.name
}))
}
getFieldsForAction = action => {
let fields = JSON.parse(JSON.stringify(this.state.fields))
fields = fields.filter(item => item.action !== null && item.action.value === action)
fields.forEach(item => {
delete item.index
delete item.sortIndex
delete item.action
})
return fields
}
getPhotosForAction = action => {
const { photos, photosUpdateMode } = this.state
if (photosUpdateMode && photosUpdateMode.value === action) {
return photos
} else {
return null
}
}
getPhonesForAction = action => {
const { phones, phonesUpdateMode } = this.state
if (phonesUpdateMode && phonesUpdateMode.value === action) {
return this.removePhoneErrorMessage(phones)
} else {
return null
}
}
getFeatureAction = name => {
const featureIds = this.state.rubricFeatures.filter(item => item.viewAttrs.name === name).map(item => item.id)
let action = null
FEATURES_MULTIEDIT_OPTIONS.some(item => {
const featureActionIds = this.state[item.value]
if (featureActionIds.some(featureActionId => featureIds.includes(featureActionId))) {
action = item
return true
}
return false
})
return action
}
handleFeatureActionChange = (featureIds, value) => {
const state = {
featuresForAppend: this.state.featuresForAppend.filter(item => !featureIds.includes(item)),
featuresForReplace: this.state.featuresForReplace.filter(item => !featureIds.includes(item)),
featuresForDelete: this.state.featuresForDelete.filter(item => !featureIds.includes(item))
}
if (value) {
state[value.value] = [...state[value.value], ...featureIds]
}
this.setState(state)
this.handleImportIgnoreClick(FEATURES_NAME_ID, true)
}
getFeaturesForAction = action => {
const featureIds = this.state[action]
const filteredFeatures = this.filterFeatures(this.state.features)
const features = []
featureIds.forEach(featureId => {
const featureIndex = filteredFeatures.findIndex(item => item.featureId === featureId)
if (featureIndex !== -1) {
features.push(filteredFeatures[featureIndex])
} else {
features.push({ featureId })
}
})
return features
}
filterFeatures = features => {
let newFeatures = JSON.parse(JSON.stringify(features))
const featuresIds = this.state.rubricFeatures.map(item => item.id)
newFeatures = newFeatures
.filter(item => featuresIds.includes(item.featureId))
.filter(item => {
if (item.nameId === FEATURE_BOOLEAN_ID) {
return !!item.value
}
if (item.nameId === FEATURE_ENUM_SINGLE_ID || item.nameId === FEATURE_ENUM_MULTIPLE_ID) {
return true
}
const keysToFilter = ['id', 'featureId', 'excludedFromYandexCheck', 'nameId', 'name']
const keys = Object.keys(item).filter(name => !keysToFilter.includes(name))
if (keys.length === 0) {
return false
}
return keys.some(key => !!item[key])
})
newFeatures.forEach(item => {
if (item.nameId === FEATURE_ENUM_SINGLE_ID || item.nameId === FEATURE_ENUM_MULTIPLE_ID) {
delete item.value
}
delete item.name
delete item.nameId
})
return newFeatures
}
filterFields = fields => {
const newFields = JSON.parse(JSON.stringify(fields.filter(item => item.value !== '')))
newFields.forEach(item => {
delete item.index
delete item.sortIndex
delete item.action
})
return newFields
}
filterGoogleAttributes = googleAttributes => {
return googleAttributes.filter(item => {
if ((item.attributeId === 1 && item.value === '') || item.value === '') {
return false
}
return item.attributeId
})
}
removePhoneErrorMessage = phones => {
const newPhone = phones.map(phone => {
delete phone.notValidMessage
return phone
})
return newPhone
}
getGoogleAttributesForAction = action => {
const googleAttributeIds = this.state[action]
const filteredGoogleAttributes = this.filterGoogleAttributes(this.state.googleAttributes)
const googleAttributes = []
googleAttributeIds.forEach(attributeId => {
//const typeId = this.getTypeIdFromAttributeId(attributeId)
const googleAttributeIndex = filteredGoogleAttributes.findIndex(item => item.attributeId === attributeId)
if (googleAttributeIndex !== -1) {
googleAttributes.push(filteredGoogleAttributes[googleAttributeIndex])
}
// У google есть тип поля список с возможностью выбрать одно значение
// но хранится его значение не как value атрибута, а как отдельный атрибут со своим уникальным id
//
/*else if (typeId !== GOOGLE_ATTRIBUTE_ENUM_SINGLE_ID) {
googleAttributes.push({ attributeId })
}*/
else {
googleAttributes.push({ attributeId })
}
})
return googleAttributes
}
/*getTypeIdFromAttributeId = id => {
const { googleCategoryAttributes } = this.state
const googleAttribute = googleCategoryAttributes.find(item => item.id === id)
return googleAttribute?.typeId
}*/
getRelatedGoogleAttributeFromId = id => {
const { googleCategoryAttributes } = this.state
const googleAttributeName = googleCategoryAttributes.find(item => item.id === id)?.name
return relatedGoogleAttributeIds = googleCategoryAttributes
.filter(item => item.name === googleAttributeName)
.map(item => item.id)
}
getGoogleAttributeAction = (id, type) => {
let action = null
const ids = []
if (type === GOOGLE_ATTRIBUTE_ENUM_SINGLE_ID) {
ids = this.getRelatedGoogleAttributeFromId(id)
}
GOOGLE_ATTRIBUTES_MULTIEDIT_OPTIONS.some(item => {
const actionIds = this.state[item.value]
if (actionIds.includes(id)) {
action = item
return true
}
return false
})
return action
}
handleGoogleAttributeActionChange = (id, value) => {
const state = {
googleAttributesForReplace: this.state.googleAttributesForReplace.filter(item => item !== id),
googleAttributesForDelete: this.state.googleAttributesForDelete.filter(item => item !== id)
}
if (value) {
state[value.value] = [...state[value.value], id]
}
this.setState(state)
}
/*handleGoogleAttributeActionChangeMulti = (id, value) => {
const { googleCategoryAttributes } = this.state
const googleAttributeName = googleCategoryAttributes.find(item => item.id === id)?.name
const googleAttributeIds = googleCategoryAttributes
.filter(item => item.name === googleAttributeName)
.map(item => item.id)
const state = {
googleAttributesForReplace: this.state.googleAttributesForReplace.filter(itemId => !googleAttributeIds.includes(itemId)),
googleAttributesForDelete: this.state.googleAttributesForDelete.filter(itemId => !googleAttributeIds.includes(itemId))
}
if (value) {
state[value.value] = [...state[value.value], ...googleAttributeIds]
}
console.log('state :>> ', state)
this.setState(state)
}*/
makePayload = () => {
const {
networkId,
comment,
photos,
phones,
rubricIds,
name,
nameLanguageCodeId,
yandexMapsLink,
googleMapsLink,
twogisMapsLink,
googleCategories,
googleLabels,
googleAttributes,
googleId,
yandexMapsId,
gmbLocationId,
twogisId,
tripadvisorId,
statusTypeId,
importExceptionTypeIds,
division,
yandexSpravId,
yandexMarketXmlImportLink,
isGoogleCheckDisabled,
isYandexXmlFeedEnabled,
isXmlImportEnabled,
hasCityOnTwogisMaps,
isDeletedOnYandexMaps,
isDeletedOnTwogis,
zoonId,
zoonLink,
yellId,
yellLink,
foursquareId,
foursquareLink,
facebookLocationId,
facebookStoreNumber,
postingDisabledProviderIds,
logoUploadId
} = this.state
const payload = {
name,
networkId,
rubricIds,
fields: this.filterFields(this.state.fields),
phones: this.removePhoneErrorMessage(phones),
photos,
features: this.filterFeatures(this.state.features),
comment,
nameLanguageCodeId,
yandexMapsLink,
googleMapsLink,
twogisMapsLink,
googleCategoryIds: googleCategories.map(item => item.value),
googleLabels: googleLabels.split('\n').filter(item => Boolean(item)),
googleAttributes: this.filterGoogleAttributes(googleAttributes),
googleId,
yandexMapsId,
gmbLocationId,
twogisId,
tripadvisorId,
statusTypeId,
importExceptionTypeIds,
division,
yandexSpravId,
yandexMarketXmlImportLink,
isGoogleCheckDisabled,
isYandexXmlFeedEnabled,
isXmlImportEnabled,
hasCityOnTwogisMaps,
isDeletedOnYandexMaps,
isDeletedOnTwogis,
zoonId,
zoonLink,
yellId,
yellLink,
foursquareId,
foursquareLink,
facebookLocationId,
facebookStoreNumber: facebookStoreNumber ? parseInt(facebookStoreNumber) : facebookStoreNumber,
postingDisabledProviderIds
}
if (logoUploadId) {
payload.logoUploadId = logoUploadId
}
return payload
}
makePayloadForMultifill = () => {
const { match } = this.props
const {
comment,
rubricIds,
name,
googleCategories,
googleLabels,
statusTypeId,
division,
isGoogleCheckDisabled,
isYandexXmlFeedEnabled,
isXmlImportEnabled,
importExceptionTypeIdsForReplace,
importExceptionTypeIdsForDelete,
rubricsForReplace,
rubricsForDelete,
phonesUpdateMode
} = this.state
const payload = {
name,
rubricIds,
comment,
googleCategoryIds: googleCategories.map(item => item.value),
googleLabels: googleLabels.split('\n').filter(item => Boolean(item)),
statusTypeId,
division,
isGoogleCheckDisabled,
isYandexXmlFeedEnabled,
isXmlImportEnabled,
importExceptionTypeIdsForReplace,
importExceptionTypeIdsForDelete,
fieldsForAppend: this.getFieldsForAction('fieldsForAppend'),
fieldsForReplace: this.getFieldsForAction('fieldsForReplace'),
fieldsForDelete: this.getFieldsForAction('fieldsForDelete'),
featuresForAppend: this.getFeaturesForAction('featuresForAppend'),
featuresForReplace: this.getFeaturesForAction('featuresForReplace'),
featuresForDelete: this.getFeaturesForAction('featuresForDelete'),
googleAttributesForReplace: this.getGoogleAttributesForAction('googleAttributesForReplace'),
googleAttributesForDelete: this.getGoogleAttributesForAction('googleAttributesForDelete'),
rubricsForReplace: rubricsForReplace.map(item => item.value),
rubricsForDelete: rubricsForDelete.map(item => item.value),
photosForAppend: this.getPhotosForAction('photosToAppend'),
photosForReplace: this.getPhotosForAction('photosToReplace'),
phonesForAppend: this.getPhonesForAction('phonesToAppend'),
phonesForReplace: this.getPhonesForAction('phonesToReplace')
}
Object.keys(payload).forEach(key => {
if (
payload[key] === null ||
payload[key] === undefined ||
payload[key] === '' ||
(typeof payload[key] === 'object' && payload[key].length === 0)
) {
delete payload[key]
}
})
// Передаем в payload пустой массив для удаления всех телефонов
if (phonesUpdateMode && phonesUpdateMode.value === 'phonesToDelete') {
payload.phonesForReplace = []
}
if (match.params.companyIds && match.params.companyIds !== 'all') {
payload.companyIds = match.params.companyIds.split(',').map(item => Number(item))
} else if (match.params.companyIds === 'all') {
payload.all = true
payload.networkId = Number(match.params.networkId)
}
return payload
}
handleSubmit = () => {
const { submitDisabled } = this.state
const isMultifill = this.isMultifill()
let payload = {}
if (submitDisabled) {
return
}
if (!isMultifill) {
payload = this.makePayload()
if (this.state.id) {
this.props.doUpdateCompany(this.state.id, payload)
} else {
this.props.doCreateCompany(payload)
}
} else {
payload = this.makePayloadForMultifill()
this.props.doMultipleUpdateCompany(payload)
}
this.setState({ submitDisabled: true })
}
getIdValidationRegex = field => {
const intFields = [
'yandexMapsId',
'yandexSpravId',
'gmbLocationId',
'twogisId',
'tripadvisorId',
'yellId',
'facebookLocationId',
'facebookStoreNumber'
]
const hexFields = ['zoonId', 'foursquareId']
const intRegex = /^[\d]+$/
const hexRegex = /^[0-9a-f]+$/
if (intFields.includes(field)) {
return intRegex
} else if (hexFields.includes(field)) {
return hexRegex
}
return null
}
getIdValidationError = field => {
const value = this.state[field]
const regex = this.getIdValidationRegex(field)
if (value && regex) {
const valid = regex.test(value)
if (!valid) {
return 'Значение поля указано в неправильном формате'
}
}
return null
}
getValidationError = field => {
const { err } = this.props
if (err !== null && err.fields !== undefined && err.fields.hasOwnProperty(field)) {
const error = err.fields[field]
return typeof error === 'object' ? JSON.stringify(error) : error
}
const idValidationError = this.getIdValidationError(field)
if (idValidationError) {
return idValidationError
}
return false
}
getValidationClass = field => {
if (this.getValidationError(field)) {
return 'is-invalid'
}
return ''
}
getValidationErrorByBlock = (block, index, field) => {
const { err } = this.props
if (
err !== null &&
err.fields !== undefined &&
err.fields.hasOwnProperty(block) &&
err.fields[block].hasOwnProperty(index) &&
err.fields[block][index].hasOwnProperty(field)
) {
return err.fields[block][index][field]
}
return false
}
getValidationClassByBlock = (block, index, field) => {
if (this.getValidationErrorByBlock(block, index, field)) {
return 'is-invalid'
}
return ''
}
getSelectClass = field => {
if (this.getValidationError(field)) {
return 'select-bg is-invalid'
}
return 'select-bg'
}
getRubricsValue = () => {
const { rubricIds } = this.state
const { rubrics, networks, match } = this.props
const network =
networks && match?.params?.networkId
? networks.find(item => Number(item.id) === Number(match.params.networkId))
: null
const networkRubric = network?.rubric
? {
value: network.rubric.id,
label: `${network.rubric.id} ${network.rubric.nameRu}`,
isFixed: true
}
: null
const selectedRubrics = rubrics.filter(item => rubricIds.includes(item.value))
return networkRubric ? [networkRubric, ...selectedRubrics] : selectedRubrics
}
handleRemoveCompany = () => {
const { match, history } = this.props
if (window.confirm('Вы действительно хотите удалить компанию?')) {
this.props.doDeleteCompany(match.params.companyId).then(() => {
history.push(`/networks/${match.params.networkId}`, {})
})
}
}
handleAddPhoneClick = e => {
e?.preventDefault && e.preventDefault()
const phones = [
...this.state.phones,
{
number: null,
ext: null,
info: null,
typeId: null,
countryNumericCode: null,
providerIds: []
}
]
this.setState({ phones })
this.handleImportIgnoreClick(PHONES_NAME_ID, true)
}
handleRemovePhoneClick = (e, removeIndex) => {
e.preventDefault()
let phones = [...this.state.phones]
phones = phones.filter((item, index) => index !== removeIndex)
this.setState({ phones })
this.handleImportIgnoreClick(PHONES_NAME_ID, true)
}
getPhoneTypeOption = value => {
if (!value) {
return null
}
return PHONE_TYPES.find(item => item.value === value)
}
handlePhoneTypeChange = (value, index) => {
const phones = [...this.state.phones]
phones[index].typeId = value ? value.value : null
this.setState({ phones })
this.handleImportIgnoreClick(PHONES_NAME_ID, true)
}
handlePhoneInfoChange = (e, index) => {
const phones = [...this.state.phones]
phones[index].info = e.target.value
this.setState({ phones })
this.handleImportIgnoreClick(PHONES_NAME_ID, true)
}
handlePhoneNumberChange = (e, index) => {
const { countryCodes } = this.props
const phones = [...this.state.phones]
const countryNumericCode = phones[index]?.countryNumericCode || null
phones[index].number = e.target.value
phones[index].notValidMessage = getMessageNotValidPhoneNumber(
countryNumericCode ? countryCodes.find(option => option.value === countryNumericCode).code : null,
phones[index].number
)
this.setState({ phones })
this.handleImportIgnoreClick(PHONES_NAME_ID, true)
}
handleCountryCodeChange = (value, index) => {
const { countryCodes } = this.props
const phones = [...this.state.phones]
phones[index].countryNumericCode = value
phones[index].notValidMessage = getMessageNotValidPhoneNumber(
value ? countryCodes.find(item => value === item.value).code : '',
phones[index].number
)
this.setState({ phones })
this.handleImportIgnoreClick(PHONES_NAME_ID, true)
}
handlePhoneProvidersChange = (value, index) => {
const phones = [...this.state.phones]
phones[index].providerIds = value.map(item => item.value)
this.setState({ phones })
this.handleImportIgnoreClick(PHONES_NAME_ID, true)
}
handlePhoneExtChange = (e, index) => {
const phones = [...this.state.phones]
phones[index].ext = e.target.value
this.setState({ phones })
this.handleImportIgnoreClick(PHONES_NAME_ID, true)
}
handleAddPhotoClick = e => {
const files = Array.from(e.target.files)
files.forEach(async file => {
const result = await API.uploadFile('upload-images', { file, type: PHOTO_TYPE_COMPANY_PHOTO })
if (result?.uploads?.[0]) {
const upload = result.uploads[0]
this.setState(state => ({
photos: [...state.photos, { uploadId: upload.id, alt: null, typeId: null }],
uploaded: [...state.uploaded, { id: upload.id, upload }]
}))
} else if (result?.error?.message) {
alert(result.error.message)
}
})
}
handleRemovePhotoClick = (e, removeIndex) => {
e.preventDefault()
let photos = [...this.state.photos]
photos = photos.filter((item, index) => index !== removeIndex)
this.setState({ photos })
}
getPhotoTypeOption = photo => {
const props = PHOTO_TYPES.find(item => item.typeId === photo.typeId && item.tagId === photo.tagId)
// Для React select v3 требуются параметры в следующем формате { label: 'foo', value: 'bar'}
// поэтому нужно дополнять массив опций, если этого не сделать то все опции в <Select> будут isSelected
return props
? { ...props, value: props?.typeId.toString().concat(props.tagId ? props.tagId.toString() : '') }
: null
}
handlePhotoTypeChange = (type, index) => {
const photos = [...this.state.photos]
photos[index].typeId = type.typeId
photos[index].tagId = type.tagId
this.setState({ photos })
}
handlePhotoAltChange = (e, index) => {
const photos = [...this.state.photos]
photos[index].alt = e.target.value
this.setState({ photos })
}
handleUploadPhoto = (e, index, type) => {
e.preventDefault()
const file = e.target.files[0]
const image = new Image(file)
image.src = URL.createObjectURL(file)
image.onload = () => {
this.props
.doUploadImage({ file, type }, index)
.then(res => {
const newUpload = res.uploads.map(item => ({ id: item.id, upload: item }))
const uploaded = [...this.state.uploaded, ...newUpload]
const photos = [...this.state.photos]
const state = { uploaded, photos }
state.photos[index].uploadId = newUpload[0].id
this.setState(state)
})
.catch(e => {
alert(e)
})
}
}
getPhotoUrl = item => {
let id = item.id
if (id === undefined) {
id = item.uploadId
}
const upload = this.state.uploaded.find(item => item.id === id)
if (upload?.upload?.image) {
return upload.upload.image
} else if (upload.externalLink) {
return {
smallUrl: upload.externalLink,
originalUrl: upload.externalLink
}
}
return null
}
handleRubricsChange = async value => {
const { networks, match } = this.props
const network =
networks && match?.params?.networkId
? networks.find(item => Number(item.id) === Number(match.params.networkId))
: null
const networkRubricId = network?.rubric?.id || null
const rubricIds = value
.filter(item => item.value !== networkRubricId)
.map(item => item.value)
.slice(0, 2)
const rubrics = [...rubricIds, networkRubricId]
const resultFields = await API.request(`features?rubricIds=[${rubrics.join(',')}]&limit=all`)
const resultFieldsFeatures = resultFields?.features || []
this.setState({
rubricIds,
rubricFeatures: resultFieldsFeatures,
rubricFeaturesOptions: this.makeRubricFeaturesOptions(resultFieldsFeatures)
})
this.handleImportIgnoreClick(RUBRICS_NAME_ID, true)
}
handleFeaturePropertyChange = (value, id, property) => {
const { features, rubricFeatures } = this.state
const featureIndex = features.findIndex(item => Number(item.featureId) === Number(id))
const parsedValue = property === 'excludedFromYandexCheck' ? value : value.toString()
if (featureIndex !== -1) {
const featuresItem = JSON.parse(JSON.stringify(features[featureIndex]))
featuresItem[property] = parsedValue
features[featureIndex] = featuresItem
} else {
const feature = rubricFeatures.find(item => Number(item.id) === Number(id))
features.push({
featureId: id,
nameId: feature.element.nameId,
name: feature.viewAttrs.hasOwnProperty('name') ? feature.viewAttrs.name : null,
[property]: parsedValue
})
}
this.setState({ features: JSON.parse(JSON.stringify(features)) })
this.handleImportIgnoreClick(FEATURES_NAME_ID, true)
}
handleFeaturePropertyChangeByName = (value, name, property) => {
const { features } = this.state
const featureIndex = features.findIndex(item => item.name === name)
const parsedValue = property === 'excludedFromYandexCheck' ? value : value.toString()
if (featureIndex !== -1) {
const featuresItem = JSON.parse(JSON.stringify(features[featureIndex]))
featuresItem[property] = parsedValue
features[featureIndex] = featuresItem
this.setState({ features: JSON.parse(JSON.stringify(features)) })
this.handleImportIgnoreClick(FEATURES_NAME_ID, true)
}
}
handleLanguageTabChange = lang => {
this.setState({ activeLanguageTab: lang })
}
handleFieldChange = (e, fieldIndex, languageCodeId, fieldId) => {
const { fields } = this.state
const index = fields.findIndex(item => item.index === fieldIndex)
const field = JSON.parse(JSON.stringify(fields[index]))
field.value = e.target.value
fields[index] = field
const state = { fields }
if (languageCodeId === this.state.nameLanguageCodeId && fields[index].nameId === 2) {
state.name = e.target.value
}
this.setState(state)
this.handleImportIgnoreClick(fieldId, true)
}
handleCoordinatesChange = ({ lng, lat }) => {
const { fields } = this.state
const indexLng = fields.findIndex(item => item.nameId === LONGITUDE_ID)
const indexLat = fields.findIndex(item => item.nameId === LATITUDE_ID)
const fieldLng = JSON.parse(JSON.stringify(fields[indexLng]))
const fieldLat = JSON.parse(JSON.stringify(fields[indexLat]))
fieldLng.value = lng
fieldLat.value = lat
fields[indexLng] = fieldLng
fields[indexLat] = fieldLat
const state = { fields }
this.setState(state)
this.handleImportIgnoreClick(LONGITUDE_ID, true)
this.handleImportIgnoreClick(LATITUDE_ID, true)
}
handleIsOrgmodChange = e => {
const fields = JSON.parse(JSON.stringify(this.state.fields))
const workTimeFields = fields.filter(field => WORKING_TIME_IDS.includes(field.nameId))
const isOrgmodOn = !workTimeFields.some(field => field.value !== ORGMOD_VALUE)
const valueReplace = FIELDS_MULTIEDIT_OPTIONS.find(option => option.value === 'fieldsForReplace')
workTimeFields.forEach(workTimeField => {
const index = fields.findIndex(field => field.nameId === workTimeField.nameId)
fields[index].value = isOrgmodOn ? '' : ORGMOD_VALUE
if (fields[index].action !== undefined) {
fields[index].action = valueReplace
}
})
this.setState({ fields })
}
handleFieldActionChange = (value, fieldIndex) => {
const fields = JSON.parse(JSON.stringify(this.state.fields))
const index = fields.findIndex(item => item.index === fieldIndex)
fields[index].action = value
const state = { fields }
this.setState(state)
}
getHeadingText() {
const { networks, match } = this.props
const { name } = this.state
const isMultifill = this.isMultifill()
if (name) {
return name
} else if (networks && match?.params?.networkId) {
const network = networks.find(item => Number(item.id) === Number(match.params.networkId))
if (network?.name && isMultifill) {
return `Мультизаполнение сети ${network.name}`
}
}
return false
}
handleAddField = (e, languageCodeId, nameId) => {
e.preventDefault()
const fields = JSON.parse(JSON.stringify(this.state.fields))
fields.push({
languageCodeId,
nameId,
value: '',
index: fields.length
})
this.setState({ fields })
}
getFieldValue = (value, nameId, languageCodeId, isNetworkField = false) => {
const { name, nameLanguageCodeId } = this.state
if (nameId === 2 && languageCodeId === nameLanguageCodeId && !isNetworkField) {
return name
}
return value || ''
}
handleCreatePriceListFromXmlClick = async () => {
const { match } = this.props
if (match?.params?.networkId) {
const result = await API.request(`companies/${match.params.companyId}/create-price-list-from-xml`, 'POST')
let message = ''
if (result?.error?.message) {
message = result.error.message
if (result?.error?.details?.length) {
message = `${message}\nДетали:\n${result.error.details.join('\n')}`
}
} else {
message = 'Запущен процесс создания прайс листа\n'
if (result?.errors?.length) {
message = `${message}\nОшибки:\n${result.errors.join('\n')}`
}
if (result?.warnings?.length) {
message = `${message}\nПредупреждения:\n${result.warnings.join('\n')}`
}
}
alert(message)
}
}
handleFeatureEnumSingleChange = (name, value) => {
const { rubricFeatures } = this.state
const features = this.state.features.filter(item => item.name !== name)
const foundFeature = rubricFeatures.find(item => (value?.value ? item.id === value.value : false))
let state = { features: [...features] }
if (foundFeature) {
const feature = {
featureId: foundFeature.id,
nameId: foundFeature.element.nameId,
name: foundFeature.viewAttrs.hasOwnProperty('name') ? foundFeature.viewAttrs.name : null,
value: foundFeature.viewAttrs.hasOwnProperty('value') ? foundFeature.viewAttrs.value : null,
...foundFeature.inputAttrs
}
state = { features: JSON.parse(JSON.stringify([...state.features, feature])) }
}
this.setState(state)
this.handleImportIgnoreClick(FEATURES_NAME_ID, true)
}
handleFeatureEnumMultipleChange = (name, value) => {
const { rubricFeatures } = this.state
const features = this.state.features.filter(item => item.name !== name)
const enumFeatures = []
value.forEach(item => {
const feature = rubricFeatures.find(feature => feature.id === item.value)
enumFeatures.push({
featureId: feature.id,
nameId: feature.element.nameId,
name: feature.viewAttrs.hasOwnProperty('name') ? feature.viewAttrs.name : null,
value: feature.viewAttrs.hasOwnProperty('value') ? feature.viewAttrs.value : null,
...feature.inputAttrs
})
})
this.setState({ features: JSON.parse(JSON.stringify([...features, ...enumFeatures])) })
this.handleImportIgnoreClick(FEATURES_NAME_ID, true)
}
isFieldRequired = nameId => {
const requiredFields = [
2,
4,
5,
6,
7,
8,
9,
10,
11,
12,
18,
20,
26,
27,
101,
103,
104,
105,
106,
107,
108,
112,
113,
114,
115,
116,
117,
118,
119,
120,
121,
122
]
if (requiredFields.some(elem => elem === nameId)) {
return true
}
return false
}
handleGoogleCategoriesChange = async value => {
const googleCategories = value.filter(item => !item.isFixed).slice(0, 9)
this.setState({ googleCategories })
}
handleGoogleAttributeValueChange = (value, attribute) => {
const googleAttributes = [...this.state.googleAttributes]
const index = googleAttributes.findIndex(item => item.attributeId === attribute.id)
if (index !== -1) {
googleAttributes[index].value = value.toString()
} else {
googleAttributes.push({
attributeId: attribute.id,
typeId: attribute.typeId,
value: value.toString()
})
}
this.setState({ googleAttributes })
}
getGoogleCategories = (googleCategories = this.state.googleCategories) => {
const { networks, match } = this.props
const network =
networks && match?.params?.networkId
? networks.find(item => Number(item.id) === Number(match.params.networkId))
: null
if (network?.googleCategory) {
const networkCategory = {
value: network.googleCategory.id,
label: network.googleCategory.name,
isFixed: true
}
return [networkCategory, ...googleCategories]
}
return googleCategories
}
getGoogleAttributeEnumOptions = name => {
const { googleCategoryAttributes } = this.state
return googleCategoryAttributes
.filter(item => item.name === name)
.map(item => ({ value: item.id, label: formatGoogleAttributeLabel(item) }))
}
handleGoogleAttributeEnumChange = (name, value, multieditValue) => {
const { googleCategoryAttributes } = this.state
const googleAttributeIds = this.state.googleCategoryAttributes
.filter(item => item.name === name)
.map(item => item.id)
const googleAttributes = this.state.googleAttributes.filter(
item => !googleAttributeIds.includes(item.attributeId)
)
const googleAttributesForAction = this.state[multieditValue].filter(item => googleAttributeIds.includes(itemId))
if (value) {
const attribute = googleCategoryAttributes.find(attribute => attribute.id === value.value)
this.setState({ [multieditValue]: [...googleAttributesForAction, attribute.id], googleAttributes: [...googleAttributes, { attributeId: attribute.id }] })
} else {
this.setState({ [multieditValue]: googleAttributesForAction, googleAttributes: [...googleAttributes] })
}
}
getGoogleAttributeEnumValue = name => {
const { googleAttributes, googleCategoryAttributes } = this.state
const attributes = googleCategoryAttributes.filter(item => item.name === name)
const attribute = attributes.find(attribute => googleAttributes.some(item => attribute.id === item.attributeId))
if (attribute) {
return {
value: attribute.id,
label: formatGoogleAttributeLabel(attribute)
}
}
return null
}
getFieldsGroupLabel = type => {
switch (type) {
case TYPE_ADDRESS:
return 'Адрес'
case TYPE_WORKING_TIME:
return 'Часы работы'
case TYPE_BREAK_TIME:
return 'Перерывы'
default:
return false
}
}
onPhotoSortEnd = ({ oldIndex, newIndex }) => {
const { photos } = this.state
this.setState({
photos: arrayMove(photos, oldIndex, newIndex)
})
}
onPhoneSortEnd = ({ oldIndex, newIndex }) => {
const { phones } = this.state
this.setState({
phones: arrayMove(phones, oldIndex, newIndex)
})
}
getGoogleAttributeBooleanValue = item => {
if (item) {
const value = Number(item.value)
if (value === 0) {
return { value: 0, label: 'Нет' }
} else if (value === 1) {
return { value: 1, label: 'Да' }
}
}
return null
}
getNetwork() {
const { networks, match } = this.props
if (networks && match?.params?.networkId) {
const network = networks.find(item => Number(item.id) === Number(match.params.networkId))
if (network) {
return network
}
}
return false
}
handleFeatureMultipleValueClick = item => {
this.handleFeaturePropertyChange(!item.excludedFromYandexCheck, item.value, 'excludedFromYandexCheck')
}
getGMBErrorTranslation = text => {
const translation = gmbErrorsTranslations[text]
return translation ? translation : text
}
handleUploadPhotosToGoogle = async () => {
const { match } = this.props
const result = await API.request(`companies/${match.params.companyId}/gmb-photos-upload`, 'POST')
if (result?.error?.details) {
alert(result.error.details.join('; '))
} else if (result) {
alert('Задача на загрузку фото в Google успешно создана')
} else {
alert('Произошла неизвестная ошибка, попробуйте повторить позже')
}
}
handleUploadPhotosToTwoGis = async () => {
const { match } = this.props
const result = await API.request(`companies/${match.params.companyId}/twogis-photos-upload`, 'POST')
if (result?.error?.details) {
alert(result.error.details.join('; '))
} else if (result) {
alert('Задача на загрузку фото в 2ГИС успешно создана')
} else {
alert('Произошла неизвестная ошибка, попробуйте повторить позже')
}
}
handleInternalYandexXmlId = async () => {
if (window.confirm('Вы действительно хотите сменить Yandex XML ID?')) {
const { match } = this.props
const result = await API.request(`companies/${match.params.companyId}/internal-yandex-xml-id`, 'PATCH')
if (result?.error?.details) {
alert(result.error.details.join('; '))
} else if (result) {
this.props.doLoadCompany(match.params.companyId)
alert('Yandex XML ID успешно изменен')
} else {
alert('Произошла неизвестная ошибка, попробуйте повторить позже')
}
}
}
handleFetchTwogisIDs = async () => {
const { match } = this.props
const result = await API.request(`companies/${match.params.companyId}/start-2gis-sync-id`, 'POST')
if (result?.error?.details) {
alert(result.error.details.join('; '))
} else if (result) {
alert('Задача на поиск ID филиала в 2ГИС создана')
} else {
alert('Произошла неизвестная ошибка, попробуйте повторить позже')
}
}
handleFetchTripadvisorIDs = async () => {
const { match } = this.props
const result = await API.request(`companies/${match.params.companyId}/start-tripadvisor-sync-id`, 'POST')
if (result?.error?.details) {
alert(result.error.details.join('; '))
} else if (result) {
alert('Задача на поиск ID филиала в Tripadvisor создана')
} else {
alert('Произошла неизвестная ошибка, попробуйте повторить позже')
}
}
handleImportIgnoreClick = (nameId, onlyAdd = false) => {
let importExceptionTypeIds = JSON.parse(JSON.stringify(this.state.importExceptionTypeIds))
const index = importExceptionTypeIds.findIndex(item => item === nameId)
if (index !== -1 && !onlyAdd) {
importExceptionTypeIds = importExceptionTypeIds.filter(item => item !== nameId)
} else {
importExceptionTypeIds.push(nameId)
}
this.setState({ importExceptionTypeIds })
}
handleCompanyImportExceptionsForReplaceChange = value => {
this.setState({ importExceptionTypeIdsForReplace: value.map(item => item.value) })
}
handleCompanyImportExceptionsForDeleteChange = value => {
this.setState({ importExceptionTypeIdsForDelete: value.map(item => item.value) })
}
handleDeleteAllTasks = async () => {
const { match } = this.props
const result = await API.request(`companies/${match.params.companyId}/tasks`, 'DELETE')
if (result?.error?.message) {
alert(result.error.message)
} else if (result) {
alert('Все задачи для данного филиала были отменены')
} else {
alert('Произошла неизвестная ошибка, попробуйте повторить позже')
}
}
getImportExceptionTypeIds = () => {
const { companyImportExceptionsOptions } = this.props
const { importExceptionTypeIds } = this.state
return companyImportExceptionsOptions.filter(item => importExceptionTypeIds.includes(item.value))
}
isGoogleAttributeDisabled = googleAttributeAction => {
const isMultifill = this.isMultifill()
if (!isMultifill || (googleAttributeAction && googleAttributeAction.value !== 'googleAttributesForDelete')) {
return false
}
return true
}
handleRebaseCompanyClick = () => {
const { id, name } = this.state
this.rebaseCompanyModalRef.show({ companyId: id, companyName: name })
}
getValueForSelectFieldName = attribute => {
return {
value: attribute.id,
label: formatGoogleAttributeLabel(attribute)
}
}
renderGoogleAttribute = (attribute, renderedGoogleAttributes) => {
const { googleAttributes } = this.state
const attributeItem = googleAttributes.find(item => item.attributeId === attribute.id)
const isMultifill = this.isMultifill()
const multieditValue = isMultifill ? this.getGoogleAttributeAction(attribute.id, attribute.type) : null
const isGoogleAttributeDisabled = this.isGoogleAttributeDisabled(multieditValue)
if (renderedGoogleAttributes.includes(attribute.name) && attribute.typeId !== 4) {
return null
}
renderedGoogleAttributes.push(attribute.name)
return (
<div className="row-center feature-row" key={attribute.id}>
<FormGroup className="form-group-inline">
<FormGroup className="form-group-inline">
<div className="form-control form-control__textBlock_liteGray" readOnly>
<span>{this.getValueForSelectFieldName(attribute).label}</span>
</div>
</FormGroup>
<OptimizedSelect
options={GOOGLE_ATTRIBUTE_TYPES}
placeholder="Тип поля"
value={GOOGLE_ATTRIBUTE_TYPES.find(item => item.value === attribute.typeId)}
isDisabled
isClearable={true}
/>
{((attribute && attribute.typeId === GOOGLE_ATTRIBUTE_BOOLEAN_ID) || (attribute && attribute.typeId === GOOGLE_ATTRIBUTE_BOOLEAN_MULTIPLE_ID)) && (
<SelectGoogleAttributeYesNo
onChange={this.handleGoogleAttributeValueChange}
currentValue={this.getGoogleAttributeBooleanValue(attributeItem)}
disabled={isGoogleAttributeDisabled}
attribute={attribute}
/>
)}
{attribute && attribute.typeId === GOOGLE_ATTRIBUTE_TEXT_SINGLE_ID && (
<div>
<InputGoogleAttributeText
onChange={this.handleGoogleAttributeValueChange}
currentValue={attributeItem?.value || ''}
disabled={isGoogleAttributeDisabled}
attribute={attribute}
/>
</div>
)}
{attribute && attribute.typeId === GOOGLE_ATTRIBUTE_ENUM_SINGLE_ID && (
<SelectGoogleAttributeEnum
options={this.getGoogleAttributeEnumOptions(attribute.name)}
onChange={this.handleGoogleAttributeEnumChange}
currentValue={this.getGoogleAttributeEnumValue(attribute.name)}
disabled={isGoogleAttributeDisabled}
attribute={attribute}
multieditValue={multieditValue}
/>
)}
{isMultifill && (
<SelectGoogleAttributeReplace
currentValue={multieditValue}
onChange={this.handleGoogleAttributeActionChange}
attribute={attribute}
/>
)}
</FormGroup>
</div>
)
}
handleRubricsForReplaceChange = rubricsForReplace => {
const rubricsForReplaceIds = rubricsForReplace.map(item => item.value)
const rubricsForDelete = this.state.rubricsForDelete.filter(item => !rubricsForReplaceIds.includes(item.value))
this.setState({ rubricsForReplace, rubricsForDelete }, async () => {
const network = this.getNetwork()
const rubricsForLoad = [network.rubric.id, ...rubricsForReplaceIds]
const resultFields = await API.request(`features?rubricIds=[${rubricsForLoad.join(',')}]&limit=all`)
const resultFieldsFeatures = resultFields?.features || []
this.setState({
rubricFeatures: resultFieldsFeatures,
rubricFeaturesOptions: this.makeRubricFeaturesOptions(resultFieldsFeatures)
})
})
}
handleRubricsForDeleteChange = rubricsForDelete => {
const rubricsForDeleteIds = rubricsForDelete.map(item => item.value)
const rubricsForReplace = this.state.rubricsForReplace.filter(item => !rubricsForDeleteIds.includes(item.value))
this.setState({ rubricsForReplace, rubricsForDelete })
}
handleProviderDisabledButtonClick = provider => {
let { postingDisabledProviderIds } = this.state
if (postingDisabledProviderIds.includes(provider.value)) {
postingDisabledProviderIds = postingDisabledProviderIds.filter(item => item !== provider.value)
} else {
postingDisabledProviderIds.push(provider.value)
}
this.setState({ postingDisabledProviderIds })
}
getColorProviderDisabledButton = providerId => {
const { postingDisabledProviderIds, statusTypeId } = this.state
if (!postingDisabledProviderIds.includes(providerId)) {
return 'success'
} else if (statusTypeId === COMPANY_STATUS_CLOSED) {
return 'secondary'
}
return 'danger'
}
toggleLogoPopover = () => {
this.setState(state => ({
logoPopoverOpen: !state.logoPopoverOpen
}))
}
getLogoError = () => {
const { upload } = this.props
if (upload?.err?.error?.message) {
return upload.err.error.message
}
return false
}
handleLogoClick = () => {
if (this.logoInput) {
this.logoInput.current.click()
}
}
handleUploadLogo = (e, index, type) => {
e.preventDefault()
const file = e.target.files[0]
const image = new Image(file)
image.src = URL.createObjectURL(file)
image.onload = () => {
this.props
.doUploadLogo({ file, type }, index)
.then(res => {
const { uploads } = res
const logoUploadId = uploads[0].id
const logoUrl = uploads[0].image.smallUrl
const logoOriginalUrl = uploads[0].image.originalUrl
this.setState({ logoUploadId, logoUrl, logoOriginalUrl })
})
.catch(e => {
alert(e)
})
}
}
handlePhotosUpdateModeChange = mode => {
this.setState({ photosUpdateMode: mode })
}
handlePhonesUpdateModeChange = mode => {
if (mode && mode.value !== 'phonesToDelete') {
this.setState({
phonesUpdateMode: mode
})
if (this.state.phones.length === 0) {
this.handleAddPhoneClick()
}
} else if (mode && mode.value === 'phonesToDelete') {
this.setState({
phonesUpdateMode: mode,
phones: []
})
}
}
handleCompanyStatusChange = value => {
this.setState({ statusTypeId: value.value })
this.handleImportIgnoreClick(STATUS_NAME_ID, true)
}
renderCompanyFields = () => {
const {
name,
comment,
yandexMapsLink,
googleMapsLink,
twogisMapsLink,
googleLabels,
googleId,
yandexMapsId,
gmbLocationId,
twogisId,
tripadvisorId,
statusTypeId,
division,
yandexSpravId,
isGoogleCheckDisabled,
isYandexXmlFeedEnabled,
isXmlImportEnabled,
hasCityOnTwogisMaps,
isDeletedOnYandexMaps,
isDeletedOnTwogis,
importExceptionTypeIds,
importExceptionTypeIdsForDelete,
importExceptionTypeIdsForReplace,
rubricsForReplace,
rubricsForDelete,
zoonId,
zoonLink,
yellId,
yellLink,
foursquareId,
foursquareLink,
facebookLocationId,
facebookStoreNumber,
yandexId,
internalYandexXmlId,
yandexMarketXmlImportLink,
hasYandexMarketXmlImportLink,
enabledProviders
} = this.state
const {
rubrics,
googleCategoriesOptions,
companyStatuses,
providers,
companyImportExceptionsOptions
} = this.props
const yandexRubric = this.getRubricsValue()
const googleCategories = this.getGoogleCategories()
const network = this.getNetwork()
const isMultifill = this.isMultifill()
return (
<>
<div className="settings-block">
<Label className="semi-bold">Настройки филиала</Label>
<FormGroupCheckbox
name="isYandexXmlFeedEnabled"
label="Не передавать данные о филиале в фиды для Яндекс.Карт"
checked={isYandexXmlFeedEnabled}
onChange={this.handleFormFieldChange}
inverted
disabledLabel
confirm
/>
<FormGroupCheckbox
name="isXmlImportEnabled"
label="Обновлять филиал при импорте из XML"
checked={isXmlImportEnabled}
onChange={this.handleFormFieldChange}
disabledLabel
confirm
addonAfter={
<TooltipComponent text="Если галочка проставлена, а филиала в XML нет - он будет удален при импорте. Для гугловских филиалов галочка должна быть снята.">
<button type="button" className="popover-link" style={TOOLTIP_DESCRIPTION_STYLE}>
?
</button>
</TooltipComponent>
}
/>
<FormGroupCheckbox
name="isGoogleCheckDisabled"
label={'"Крымский" филиал: не обновлять и не проверять на картах Google и 2ГИС'}
checked={isGoogleCheckDisabled}
onChange={this.handleFormFieldChange}
disabledLabel
confirm
addonAfter={
<TooltipComponent text="В личном кабинете будут скрыты блоки со статусом на картах Google и 2ГИС">
<button type="button" className="popover-link" style={TOOLTIP_DESCRIPTION_STYLE}>
?
</button>
</TooltipComponent>
}
/>
{!isMultifill && (
<>
<FormGroupCheckbox
name="hasCityOnTwogisMaps"
label="Города нет на 2ГИС"
checked={hasCityOnTwogisMaps}
onChange={this.handleFormFieldChange}
addonAfter={
<TooltipComponent text="В личном кабинете будет скрыта кнопка Ответить для 2ГИС">
<button
type="button"
className="popover-link"
style={TOOLTIP_DESCRIPTION_STYLE}
>
?
</button>
</TooltipComponent>
}
inverted
disabledLabel
confirm
/>
<FormGroupCheckbox
name="isDeletedOnYandexMaps"
label="Филиал закрыт, карточка удалена с Яндекс.Карт"
checked={isDeletedOnYandexMaps}
onChange={this.handleFormFieldChange}
disabled={statusTypeId !== COMPANY_STATUS_CLOSED}
disabledLabel
confirm
/>
<FormGroupCheckbox
name="isDeletedOnTwogis"
label="Филиал закрыт, карточка удалена с 2ГИС"
checked={isDeletedOnTwogis}
onChange={this.handleFormFieldChange}
disabled={statusTypeId !== COMPANY_STATUS_CLOSED}
disabledLabel
confirm
/>
<FormGroup className="form-group-label">
<Label>Платформы, на которых включена отправка ответов</Label>
<div>
{providers
.filter(
item =>
item.value !== YANDEX_PROVIDER_ID &&
item.value !== GOOGLE_PROVIDER_ID &&
item.value !== OTZOVIK_PROVIDER_ID &&
item.value !== IRECOMMEND_PROVIDER_ID &&
item.value !== GRUZINSKIE_KANIKULI_PROVIDER_ID &&
!item.isCustom
)
.map(provider => (
<ProviderDisabledButton
key={provider.value}
color={this.getColorProviderDisabledButton(provider.value)}
provider={provider}
onClick={this.handleProviderDisabledButtonClick}
/>
))}
</div>
</FormGroup>
</>
)}
</div>
<FormGroup>
<Label for="statusTypeId">
Статус филиала
<span className="ignore-import-button-container">
<ImportIgnoredButton
nameId={STATUS_NAME_ID}
exceptionIds={importExceptionTypeIds}
isMultifill={this.isMultifill()}
onClick={this.handleImportIgnoreClick}
/>
</span>
</Label>
<OptimizedSelect
id="statusTypeId"
options={companyStatuses}
className={this.getSelectClass('statusTypeId')}
placeholder="Выберите статус филиала"
isClearable={false}
onChange={this.handleCompanyStatusChange}
value={companyStatuses.find(item => item.value === statusTypeId)}
/>
<div className="invalid-feedback">{this.getValidationError('statusTypeId')}</div>
</FormGroup>
<FormGroupTextInput
name="name"
label="Название"
onChange={this.handleFormFieldChange}
value={name}
tooltip={null}
type="text"
placeholder="Название компании"
error={this.getValidationError('name')}
disabled
/>
<FormGroupTextInput
key="yandexId"
name="yandexId"
label="Company ID"
value={yandexId}
type="text"
disabled
/>
<FormGroupTextInput
name="comment"
label="Комментарий"
onChange={this.handleFormFieldChange}
value={comment}
type="textarea"
placeholder="Комментарий"
error={this.getValidationError('comment')}
required
/>
<FormGroupTextInput
name="division"
label="Дивизион"
onChange={this.handleFormFieldChange}
value={division}
type="text"
placeholder="Дивизион"
error={this.getValidationError('division')}
required
/>
{enabledProviders.isYandexEnabled && (
<div className="settings-block">
<Label className="semi-bold">Яндекс</Label>
{!isMultifill && (
<>
<FormGroupTextInput
name="yandexMapsId"
label="Yandex Maps ID"
onChange={this.handleFormFieldChange}
value={yandexMapsId}
type="text"
placeholder="Yandex Maps ID"
error={this.getValidationError('yandexMapsId')}
/>
<FormGroupTextInput
name="yandexSpravId"
label="Код филиала в Яндекс.Справочнике"
onChange={this.handleFormFieldChange}
value={yandexSpravId}
type="text"
placeholder="Код филиала в Яндекс.Справочнике"
error={this.getValidationError('yandexSpravId')}
link={yandexSpravId ? `https://yandex.ru/sprav/${yandexSpravId}/edit/` : null}
/>
<FormGroupTextInput
key="yandexMapsLink"
name="yandexMapsLink"
label="Ссылка на Яндекс.Карты"
onChange={this.handleFormFieldChange}
value={yandexMapsLink}
type="text"
placeholder="Ссылка на Яндекс.Карты"
error={this.getValidationError('yandexMapsLink')}
required
link={yandexMapsLink}
disabled
/>
<FormGroupTextInput
key="internalYandexXmlId"
name="internalYandexXmlId"
label="Yandex XML ID"
value={internalYandexXmlId}
type="text"
disabled
/>
<FormGroupTextInput
key="yandexMarketXmlImportLink"
name="yandexMarketXmlImportLink"
label="Ссылка на XML прайс-листа в формате Яндекс.Маркета"
onChange={this.handleFormFieldChange}
value={yandexMarketXmlImportLink}
type="text"
placeholder="Ссылка на XML прайс-листа в формате Яндекс.Маркета"
error={this.getValidationError('yandexMarketXmlImportLink')}
addonAfter={
hasYandexMarketXmlImportLink && (
<Button
key="yandexMarketXmlImportButton"
color="outline-primary"
className="ml-2"
onClick={this.handleCreatePriceListFromXmlClick}
>
<nobr>Загрузить прайс-лист из XML</nobr>
</Button>
)
}
/>
</>
)}
{isMultifill ? (
<>
<FormGroup key="rubricsForReplace">
<Label for="rubricsForReplace">Рубрики на Яндексе (для замены)</Label>
<OptimizedSelect
id="rubricsForReplace"
options={rubrics}
className={this.getSelectClass('rubricsForReplace')}
placeholder="Рубрики"
isMulti={true}
onChange={this.handleRubricsForReplaceChange}
value={rubricsForReplace}
isClearable={true}
/>
<div className="invalid-feedback">
{this.getValidationError('rubricsForReplace')}
</div>
</FormGroup>
<FormGroup key="rubricsForDelete">
<Label for="rubricsForDelete">Рубрики на Яндексе (для удаления)</Label>
<OptimizedSelect
id="rubricsForDelete"
options={rubrics}
className={this.getSelectClass('rubricsForDelete')}
placeholder="Рубрики"
isMulti={true}
onChange={this.handleRubricsForDeleteChange}
value={rubricsForDelete}
isClearable={true}
/>
<div className="invalid-feedback">
{this.getValidationError('rubricsForDelete')}
</div>
</FormGroup>
</>
) : (
<FormGroup>
<Label for="rubrics">
Рубрики на Яндексе
<TooltipComponent text="Поле учитывается в заполненности филиала" />
<span className="ignore-import-button-container">
<ImportIgnoredButton
nameId={RUBRICS_NAME_ID}
exceptionIds={importExceptionTypeIds}
isMultifill={this.isMultifill()}
onClick={this.handleImportIgnoreClick}
/>
</span>
</Label>
<OptimizedSelect
id="rubrics"
options={rubrics}
className={this.getSelectClass('rubricIds')}
placeholder="Рубрики"
isMulti={true}
onChange={this.handleRubricsChange}
value={yandexRubric}
isClearable={yandexRubric.some(item => !item.isFixed)}
/>
<div className="invalid-feedback">{this.getValidationError('rubricIds')}</div>
</FormGroup>
)}
</div>
)}
{enabledProviders.isGoogleEnabled && (
<div className="settings-block">
<Label className="semi-bold">Google</Label>
{!isGoogleCheckDisabled && (
<>
{!isMultifill && (
<>
<FormGroupTextInput
name="googleId"
label="Код филиала Гугла"
onChange={this.handleFormFieldChange}
value={googleId}
type="text"
placeholder="Код филиала Гугла"
error={this.getValidationError('googleId')}
link={network.gmbAddressGroupLink}
/>
<FormGroupTextInput
name="gmbLocationId"
label="Код филиала GMB API (Location ID)"
onChange={this.handleFormFieldChange}
value={gmbLocationId}
type="text"
placeholder="Код филиала GMB API (Location ID)"
error={this.getValidationError('gmbLocationId')}
/>
</>
)}
</>
)}
{!isMultifill && (
<FormGroupTextInput
key="googleMapsLink"
name="googleMapsLink"
label="Ссылка на Google Карты"
onChange={this.handleFormFieldChange}
value={googleMapsLink}
type="text"
placeholder="Ссылка на Google Карты"
error={this.getValidationError('googleMapsLink')}
link={googleMapsLink}
required
/>
)}
{!isGoogleCheckDisabled && (
<>
<FormGroupTextInput
name="googleLabels"
label="Лейблы Гугла"
onChange={this.handleFormFieldChange}
value={googleLabels}
type="textarea"
placeholder="Лейблы Гугла"
error={this.getValidationError('googleLabels')}
/>
<FormGroup>
<Label for="google-categories">
Категории Гугла
<TooltipComponent text="Поле учитывается в заполненности филиала" />
</Label>
<OptimizedSelect
id="google-categories"
options={googleCategoriesOptions}
className={this.getSelectClass('googleCategoryIds')}
placeholder="Выберите категории Гугла"
isMulti={true}
onChange={this.handleGoogleCategoriesChange}
value={googleCategories}
isClearable={googleCategories.some(item => !item.isFixed)}
/>
<div className="invalid-feedback">
{this.getValidationError('googleCategoryIds')}
</div>
</FormGroup>
</>
)}
</div>
)}
{enabledProviders.isTwogisEnabled && !isMultifill && (
<div className="settings-block">
<Label className="semi-bold">2ГИС</Label>
<FormGroupTextInput
name="twogisMapsLink"
label="Ссылка на 2ГИС"
onChange={this.handleFormFieldChange}
value={twogisMapsLink}
type="text"
placeholder="Ссылка на 2ГИС"
error={this.getValidationError('twogisMapsLink')}
required
link={twogisMapsLink}
disabled
/>
<FormGroupTextInput
name="twogisId"
label="2ГИС ID"
onChange={this.handleFormFieldChange}
value={twogisId}
type="text"
placeholder="2ГИС ID"
error={this.getValidationError('twogisId')}
link={twogisMapsLink && twogisId ? twogisMapsLink : null}
/>
</div>
)}
{enabledProviders.isTripadvisorEnabled && !isMultifill && (
<div className="settings-block">
<Label className="semi-bold">Tripadvisor</Label>
<FormGroupTextInput
name="tripadvisorId"
label="Tripadvisor ID"
onChange={this.handleFormFieldChange}
value={tripadvisorId}
type="text"
placeholder="Tripadvisor ID"
error={this.getValidationError('tripadvisorId')}
/>
</div>
)}
{enabledProviders.isZoonEnabled && !isMultifill && (
<div className="settings-block">
<Label className="semi-bold">Zoon</Label>
<FormGroupTextInput
name="zoonId"
label="Zoon ID"
onChange={this.handleFormFieldChange}
value={zoonId}
type="text"
placeholder="Zoon ID"
error={this.getValidationError('zoonId')}
/>
<FormGroupTextInput
name="zoonLink"
label="Ссылка на филиал в Zoon"
onChange={this.handleFormFieldChange}
value={zoonLink}
type="text"
placeholder="Ссылка на филиал в Zoon"
error={this.getValidationError('zoonLink')}
link={zoonLink}
/>
</div>
)}
{enabledProviders.isYellEnabled && !isMultifill && (
<div className="settings-block">
<Label className="semi-bold">Yell</Label>
<FormGroupTextInput
name="yellId"
label="Yell ID"
onChange={this.handleFormFieldChange}
value={yellId}
type="text"
placeholder="Yell ID"
error={this.getValidationError('yellId')}
/>
<FormGroupTextInput
name="yellLink"
label="Ссылка на филиал в Yell"
onChange={this.handleFormFieldChange}
value={yellLink}
type="text"
placeholder="Ссылка на филиал в Yell"
error={this.getValidationError('yellLink')}
link={yellLink}
/>
</div>
)}
{enabledProviders.isFoursquareEnabled && !isMultifill && (
<div className="settings-block">
<Label className="semi-bold">Foursquare</Label>
<FormGroupTextInput
name="foursquareId"
label="Foursquare ID"
onChange={this.handleFormFieldChange}
value={foursquareId}
type="text"
placeholder="Foursquare ID"
error={this.getValidationError('foursquareId')}
/>
<FormGroupTextInput
name="foursquareLink"
label="Ссылка на филиал в Foursquare"
onChange={this.handleFormFieldChange}
value={foursquareLink}
type="text"
placeholder="Ссылка на филиал в Foursquare"
error={this.getValidationError('foursquareLink')}
link={foursquareLink}
/>
</div>
)}
{enabledProviders.isFacebookEnabled && !isMultifill && (
<div className="settings-block">
<Label className="semi-bold">Facebook</Label>
<FormGroupTextInput
name="facebookLocationId"
label="Facebook Location ID"
onChange={this.handleFormFieldChange}
value={facebookLocationId}
type="text"
placeholder="Facebook Location ID"
error={this.getValidationError('facebookLocationId')}
/>
<FormGroupTextInput
name="facebookStoreNumber"
label="Facebook Store Number"
onChange={this.handleFormFieldChange}
value={facebookStoreNumber}
type="number"
placeholder="Facebook Store Number"
error={this.getValidationError('facebookStoreNumber')}
/>
</div>
)}
{isMultifill && (
<>
<FormGroup>
<Label for="importExceptionTypeIdsForReplace">
Включить замочки для полей
<TooltipComponent text="Данные в этих полях не обновляются при импорте из внешних источников">
<button type="button" className="popover-link" style={TOOLTIP_DESCRIPTION_STYLE}>
?
</button>
</TooltipComponent>
</Label>
<OptimizedSelect
id="importExceptionTypeIdsForReplace"
options={companyImportExceptionsOptions.filter(
item => !importExceptionTypeIdsForDelete.includes(item.value)
)}
className={this.getSelectClass('importExceptionTypeIdsForReplace')}
placeholder="Выберите поля"
isMulti={true}
onChange={this.handleCompanyImportExceptionsForReplaceChange}
simpleValue
value={importExceptionTypeIdsForReplace}
isClearable={true}
/>
<div className="invalid-feedback">
{this.getValidationError('importExceptionTypeIdsForReplace')}
</div>
</FormGroup>
<FormGroup>
<Label for="importExceptionTypeIdsForDelete">
Убрать замочки для полей
<TooltipComponent text="Данные в этих полях начнут обновляться из внешних источников">
<button type="button" className="popover-link" style={TOOLTIP_DESCRIPTION_STYLE}>
?
</button>
</TooltipComponent>
</Label>
<OptimizedSelect
id="importExceptionTypeIdsForDelete"
options={companyImportExceptionsOptions.filter(
item => !importExceptionTypeIdsForReplace.includes(item.value)
)}
className={this.getSelectClass('importExceptionTypeIdsForDelete')}
placeholder="Выберите поля"
isMulti={true}
onChange={this.handleCompanyImportExceptionsForDeleteChange}
value={importExceptionTypeIdsForDelete}
isClearable={true}
simpleValue
/>
<div className="invalid-feedback">
{this.getValidationError('importExceptionTypeIdsForDelete')}
</div>
</FormGroup>
</>
)}
</>
)
}
filterLanguageCodesForFields = languageCode => LANGUAGES.find(item => item.value === languageCode)
handleFieldProviderIdsChange = (value, fieldIndex, languageCodeId, fieldId) => {
const { fields } = this.state
const index = fields.findIndex(item => item.index === fieldIndex)
const field = JSON.parse(JSON.stringify(fields[index]))
field.providerIds = value.map(item => item.value)
fields[index] = field
this.setState({ fields })
this.handleImportIgnoreClick(fieldId, true)
}
renderLanguageCodePane = languageCode => {
const { providers } = this.props
const { activeLanguageTab } = this.state
const network = this.getNetwork()
const isMultifill = this.isMultifill()
if (languageCode !== activeLanguageTab) {
return null
}
return (
<TabPane tabId={languageCode} key={languageCode}>
<FieldsBlock
isMultifill={isMultifill}
languageCode={languageCode}
propsFields={this.props.fields}
stateFields={this.state.fields}
isFieldRequired={this.isFieldRequired}
getFieldValue={this.getFieldValue}
getFieldsGroupLabel={this.getFieldsGroupLabel}
handleAddField={this.handleAddField}
handleFieldChange={this.handleFieldChange}
handleCoordinatesChange={this.handleCoordinatesChange}
handleIsOrgmodChange={this.handleIsOrgmodChange}
handleImportIgnoreClick={this.handleImportIgnoreClick}
importExceptionTypeIds={this.state.importExceptionTypeIds}
handleFieldActionChange={this.handleFieldActionChange}
networkFields={network.fields}
providers={providers}
handleFieldProviderIdsChange={this.handleFieldProviderIdsChange}
/>
</TabPane>
)
}
renderFields = () => {
const { languageCodeIds, activeLanguageTab } = this.state
if (languageCodeIds.length === 0) {
return null
}
return (
<div className="settings-block">
<FormGroup className="form-group-label">
<Label className="semi-bold">Основные поля</Label>
</FormGroup>
<Nav tabs className="nav-tabs-fields">
{languageCodeIds.filter(this.filterLanguageCodesForFields).map(languageCode => {
const lang = LANGUAGES.find(item => item.value === languageCode)
return (
<NavLang
key={`lang-${languageCode}`}
languageCode={languageCode}
active={activeLanguageTab}
onClick={this.handleLanguageTabChange}
label={lang.label}
/>
)
})}
</Nav>
<TabContent className="tab-fields" activeTab={activeLanguageTab}>
{languageCodeIds.filter(this.filterLanguageCodesForFields).map(this.renderLanguageCodePane)}
</TabContent>
</div>
)
}
renderPhones = () => {
const { phones, phonesUpdateMode, importExceptionTypeIds } = this.state
const { countryCodes, providers } = this.props
const isMultifill = this.isMultifill()
const phoneProviders = providers.filter(
provider =>
provider.value === YANDEX_PROVIDER_ID ||
provider.value === GOOGLE_PROVIDER_ID ||
provider.value === TWOGIS_PROVIDER_ID
)
const showPhonesBlock = phones.length > 0 || (phonesUpdateMode && phonesUpdateMode.value === 'phonesToDelete')
if (!showPhonesBlock) {
return null
}
return (
<div className="settings-block">
<FormGroup className="form-group-label">
<Label className="semi-bold">
Телефоны
<TooltipComponent text="Поле учитывается в заполненности филиала" />
</Label>
<span className="ignore-import-button-container">
<ImportIgnoredButton
nameId={PHONES_NAME_ID}
exceptionIds={importExceptionTypeIds}
isMultifill={this.isMultifill()}
onClick={this.handleImportIgnoreClick}
/>
</span>
</FormGroup>
{isMultifill && (
<div className="col-xs-12 col-lg-4 mb-3 pl-0">
<OptimizedSelect
options={PHONE_UPDATE_MODES}
onChange={this.handlePhonesUpdateModeChange}
value={this.state.phonesUpdateMode}
placeholder="Оставить как есть"
isClearable={true}
/>
</div>
)}
<PhoneSortableList
useDragHandle={true}
items={phones}
countryCodes={countryCodes}
providers={phoneProviders}
onSortEnd={this.onPhoneSortEnd}
getPhoneTypeOption={this.getPhoneTypeOption}
handlePhoneTypeChange={this.handlePhoneTypeChange}
handlePhoneInfoChange={this.handlePhoneInfoChange}
handlePhoneNumberChange={this.handlePhoneNumberChange}
handleCountryCodeChange={this.handleCountryCodeChange}
handlePhoneExtChange={this.handlePhoneExtChange}
handleRemovePhoneClick={this.handleRemovePhoneClick}
handlePhoneProvidersChange={this.handlePhoneProvidersChange}
/>
{phones.length > 0 && [
<Button key="button" color="primary" size="sm" className="mr-2" onClick={this.handleAddPhoneClick}>
Добавить телефон
</Button>
]}
</div>
)
}
renderFeatures = () => {
const { rubricFeatures, rubricFeaturesOptions, importExceptionTypeIds } = this.state
const { featuresOptions, err } = this.props
if (rubricFeatures.length === 0) {
return null
}
return (
<div className="settings-block">
<FormGroup className="form-group-label">
<Label className="semi-bold">Поля рубрик</Label>
<span className="ignore-import-button-container">
<ImportIgnoredButton
nameId={FEATURES_NAME_ID}
exceptionIds={importExceptionTypeIds}
isMultifill={this.isMultifill()}
onClick={this.handleImportIgnoreClick}
/>
</span>
</FormGroup>
<FeaturesBlock
err={err}
rubricFeatures={rubricFeatures}
rubricFeaturesOptions={rubricFeaturesOptions}
propsFeatures={this.props.features}
featureIdOptions={featuresOptions}
stateFeatures={this.state.features}
handleFeatureEnumSingleChange={this.handleFeatureEnumSingleChange}
handleFeaturePropertyChangeByName={this.handleFeaturePropertyChangeByName}
handleFeatureEnumMultipleChange={this.handleFeatureEnumMultipleChange}
handleFeatureMultipleValueClick={this.handleFeatureMultipleValueClick}
handleFeaturePropertyChange={this.handleFeaturePropertyChange}
isMultifill={this.isMultifill()}
getFeatureAction={this.getFeatureAction}
handleFeatureActionChange={this.handleFeatureActionChange}
/>
</div>
)
}
renderPhotos = () => {
const { photos, importExceptionTypeIds } = this.state
const isMultifill = this.isMultifill()
if (photos.length === 0) {
return null
}
return (
<div className="settings-block">
<FormGroup className="form-group-label">
<Label className="semi-bold">Фото</Label>
<span className="ignore-import-button-container">
<ImportIgnoredButton
nameId={PHOTOS_NAME_ID}
exceptionIds={importExceptionTypeIds}
isMultifill={this.isMultifill()}
onClick={this.handleImportIgnoreClick}
/>
</span>
</FormGroup>
{isMultifill && (
<div className="col-xs-12 col-lg-4 mb-3 pl-0">
<OptimizedSelect
options={PHOTO_UPDATE_MODES}
onChange={this.handlePhotosUpdateModeChange}
value={this.state.photosUpdateMode}
placeholder="Оставить как есть"
isClearable={true}
/>
</div>
)}
<PhotoSortableList
useDragHandle={true}
items={photos}
onSortEnd={this.onPhotoSortEnd}
getPhotoUrl={this.getPhotoUrl}
handleUploadPhoto={this.handleUploadPhoto}
getPhotoTypeOption={this.getPhotoTypeOption}
handlePhotoTypeChange={this.handlePhotoTypeChange}
handlePhotoAltChange={this.handlePhotoAltChange}
handleRemovePhotoClick={this.handleRemovePhotoClick}
/>
<div className="file-upload file-upload-primary" key="upload">
<Button key="button" color="primary" size="sm">
Добавить фото
</Button>
<input type="file" multiple="multiple" accept="image/*" onChange={this.handleAddPhotoClick} />
</div>
</div>
)
}
renderGoogleAttributes = () => {
const { googleCategoryAttributes, isGoogleCheckDisabled } = this.state
const renderedGoogleAttributes = []
if (isGoogleCheckDisabled || googleCategoryAttributes.length === 0) {
return null
}
return (
<div className="settings-block">
<FormGroup className="form-group-label">
<Label className="semi-bold">Атрибуты Гугла</Label>
</FormGroup>
{googleCategoryAttributes.map(item => this.renderGoogleAttribute(item, renderedGoogleAttributes))}
</div>
)
}
renderLogoBlock = () => {
const { logoPopoverOpen, logoUrl, logoOriginalUrl } = this.state
return (
<div className="settings-block">
<FormGroup key="logo">
<Label className="semi-bold" for="label">
Логотип{' '}
<button type="button" className="popover-link" id="logo-popover">
?
</button>
<Popover
placement="right"
isOpen={logoPopoverOpen}
target="logo-popover"
toggle={this.toggleLogoPopover}
trigger="focus"
>
<PopoverBody>
<p>
Минимальный размер логотипа 200×200 пикселей, формат файла JPG или PNG. <br></br>
Также обязательно соблюдение следующих условий:
</p>
<ul>
<li>фон логотипа должен быть контрастным, непрозрачным;</li>
<li>не допускается размещение логотипа без фона;</li>
<li>логотип должен иметь отступы от границ фона;</li>
<li>
если на логотипе есть надпись, при размещении она должна быть четкой, хорошо
читаться.
</li>
</ul>
</PopoverBody>
</Popover>
</Label>
{logoUrl ? (
<div className="upload-photo-wrapper flex-start align-items-center">
<img className="upload-photo" onClick={this.handleLogoClick} src={logoUrl} alt="" />
<a
href={logoOriginalUrl}
className="btn btn-outline-success btn-sm ml-2"
target="_blank"
rel="noopener noreferrer"
>
<FontAwesomeIcon icon="save" />
</a>
</div>
) : (
<div className="upload-photo-wrapper flex-start">
<div className="upload-photo upload-photo-placeholder" onClick={this.handleLogoClick} />
</div>
)}
<InputImage
typePhoto={PHOTO_TYPE_COMPANY_LOGO}
name="networkLogo"
onChange={this.handleUploadLogo}
ref={this.logoInput}
/>
{this.getLogoError() && (
<div className="invalid-feedback" style={INVALID_FEEDBACK_STYLE}>
{this.getLogoError()}
</div>
)}
</FormGroup>
</div>
)
}
handleFormFieldChange = (fieldName, value) => {
const state = { [fieldName]: value }
if (fieldName === 'yandexMarketXmlImportLink') {
state.hasYandexMarketXmlImportLink = false
}
this.setState(state)
}
handleRebaseModalRef = c => (this.rebaseCompanyModalRef = c)
render() {
const {
isLoading,
notFound,
id,
phones,
photos,
submitDisabled,
createdAt,
fields,
enabledProviders
} = this.state
const { loaded, task, match } = this.props
const heading = this.getHeadingText()
const isMultifill = this.isMultifill()
const companyMenu = getCompanyChangelogsMenu(match.params.networkId, match.params.companyId)
const companyIds = isMultifill
? match.params.companyIds.split(',')
: match.params.companyId
? [match.params.companyId]
: undefined
if (isLoading) {
return <Loader />
}
if (notFound) {
return (
<div className="head">
<h2>Филиал не найден</h2>
</div>
)
}
const addressHeadFields = fields.filter(
item => DISPLAY_HEADER_ADDRESS_IDS.includes(item.nameId) && item.languageCodeId === LANG_RU
)
const addressHeadValues = DISPLAY_HEADER_ADDRESS_IDS.map(id => {
const item = addressHeadFields.find(item => item.nameId === id)
if (item) {
return item.value
}
return null
})
return (
<TasksFinisher companyIds={companyIds} networkId={match.params.networkId}>
<TabMenu
links={companyMenu}
currentLink={`/networks/${match.params.networkId}/${match.params.companyId}`}
/>
{heading && (
<div className="head">
<Link
to={`/networks/${match.params.networkId}`}
title="Вернуться к списку филиалов"
className="arrow-back"
>
<FontAwesomeIcon icon="arrow-left" />
</Link>
<h2>{heading}</h2>
<div className="flex-row space-around ml-2">
{loaded?.network && (
<>
<CheckResult
networkId={loaded.network.id}
companyId={loaded.id}
item={loaded}
providerId={YANDEX_PROVIDER_ID}
type={TYPE_COMPANY_CHECK_ERRORS}
enabledProviders={enabledProviders}
/>
<CheckResult
networkId={loaded.network.id}
companyId={loaded.id}
item={loaded}
providerId={GOOGLE_PROVIDER_ID}
type={TYPE_COMPANY_CHECK_ERRORS}
enabledProviders={enabledProviders}
/>
<CheckResult
networkId={loaded.network.id}
companyId={loaded.id}
item={loaded}
providerId={TWOGIS_PROVIDER_ID}
type={TYPE_COMPANY_CHECK_ERRORS}
enabledProviders={enabledProviders}
/>
</>
)}
</div>
<div>
<Button
id="save-button"
disabled={submitDisabled || Boolean(task)}
color="primary"
size="sm"
className="mr-2 ml-0 mb-2"
onClick={this.handleSubmit}
>
Сохранить
</Button>
{!isMultifill && id && (
<ActionsDropdown title="Другие действия">
<DropdownItem header>Общие действия</DropdownItem>
<DropdownItem disabled={Boolean(task)} onClick={this.handleRebaseCompanyClick}>
Перенести филиал в другую сеть
</DropdownItem>
{Boolean(task) && (
<DropdownItem onClick={this.handleDeleteAllTasks}>
Отменить все задачи
</DropdownItem>
)}
<DropdownItem divider />
<DropdownItem header>Яндекс Карты</DropdownItem>
<DropdownItem disabled={Boolean(task)} onClick={this.handleInternalYandexXmlId}>
Сменить Yandex XML ID
</DropdownItem>
<DropdownItem divider />
<DropdownItem header>Google Карты</DropdownItem>
<DropdownItem disabled={Boolean(task)} onClick={this.handleUploadPhotosToGoogle}>
Загрузить фото в Google
</DropdownItem>
<DropdownItem divider />
<DropdownItem header>2ГИС</DropdownItem>
<DropdownItem
disabled={!enabledProviders.isTwogisEnabled}
onClick={this.handleFetchTwogisIDs}
>
Синхронизировать ID филиала
</DropdownItem>
<DropdownItem
disabled={!enabledProviders.isTwogisEnabled}
onClick={this.handleUploadPhotosToTwoGis}
>
Загрузить фото в 2ГИС
</DropdownItem>
<DropdownItem divider />
<DropdownItem header>Tripadvisor</DropdownItem>
<DropdownItem
disabled={!enabledProviders.isTripadvisorEnabled}
onClick={this.handleFetchTripadvisorIDs}
>
Синхронизировать ID филиала
</DropdownItem>
<DropdownItem divider />
<DropdownItem header>Личный кабинет</DropdownItem>
<a
className="dropdown-item"
href={`${CONFIG.LK_URL}/${match.params.networkId}/branch/${match.params.companyId}`}
target="_blank"
rel="noopener noreferrer"
>
Перейти на страницу филиала в личном кабинете
</a>
<DropdownItem divider />
<DropdownItem onClick={this.handleRemoveCompany}>Удалить компанию</DropdownItem>
</ActionsDropdown>
)}
</div>
</div>
)}
{Boolean(addressHeadValues.length) && (
<div className="company-address">
<p>{addressHeadValues.filter(item => item).join(', ')}</p>
</div>
)}
{createdAt && (
<div className="network-description">
<p>Филиал создан в {formatDateTime(createdAt)}</p>
</div>
)}
<Notifications companyId={match.params.companyId} />
<Form autoComplete="off" onSubmit={this.handleSubmit}>
{this.renderCompanyFields()}
{this.renderFields()}
{this.renderPhones()}
{this.renderFeatures()}
{this.renderGoogleAttributes()}
{!isMultifill && this.renderLogoBlock()}
{this.renderPhotos()}
<div className="buttons-row">
<Button
id="save-button"
disabled={submitDisabled || Boolean(task)}
color="primary"
size="sm"
className="mr-2 mb-2"
onClick={this.handleSubmit}
>
Сохранить
</Button>
{phones.length === 0 && (
<Button color="primary" size="sm" className="mr-2 mb-2" onClick={this.handleAddPhoneClick}>
Добавить телефон
</Button>
)}
{photos.length === 0 && (
<div className="file-upload file-upload-primary" key="upload">
<Button key="button" color="primary" size="sm" className="mb-2">
Добавить фото
</Button>
<input
type="file"
multiple="multiple"
accept="image/*"
onChange={this.handleAddPhotoClick}
/>
</div>
)}
</div>
</Form>
<div className="fixed-alert">
<BusyNetworkAlert
isOpen={Boolean(task)}
task={task}
prefix={isMultifill && 'В одном или нескольких выбранных филиалах выполняется задача: '}
/>
</div>
<RebaseCompanyModal ref={this.handleRebaseModalRef} />
</TasksFinisher>
)
}
}
const mapStateToProps = state => {
return {
action: state.action,
me: state.user.me,
loaded: state.company.loaded,
networks: state.networks.loaded,
err: state.company.err,
rubrics: state.rubrics.rubricsOptions,
countryCodes: state.countries.countriesOptions,
upload: state.upload,
fields: state.fields.loaded,
features: state.featureElements.loaded,
featuresOptions: state.featureElements.options,
googleCategoriesOptions: state.googleCategories.options,
task: state.tasks.task,
companyStatuses: state.companyStatuses.options,
providers: state.providers.options,
companyImportExceptionsOptions: state.companyImportExceptions.options
}
}
const mapDispatchToProps = dispatch => {
return bindActionCreators(
{
goBack,
...CompanyActions,
...NetworksActions,
...UploadActions,
...TasksActions,
...RubricsActions,
...FieldsActions,
...FeatureElementsActions,
...GoogleCategoriesActions,
...CountriesActions,
...ProvidersActions,
...CompanyStatusesActions,
...CompanyImportExceptionsActions
},
dispatch
)
}
export default connect(mapStateToProps, mapDispatchToProps)(withRouter(CompanyForm))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment