Created
April 9, 2021 12:19
-
-
Save renderlife/58aa5ff93cb80bdc41457ded822d8df8 to your computer and use it in GitHub Desktop.
2246
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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