plugin updates
This commit is contained in:
@@ -1,22 +0,0 @@
|
||||
import React from 'react';
|
||||
import ElementorBanner from './ElementorBanner';
|
||||
import { __ } from '@wordpress/i18n';
|
||||
|
||||
export default function ConnectPluginBanner() {
|
||||
return (
|
||||
<ElementorBanner>
|
||||
<b
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: __(
|
||||
'The HubSpot plugin is not connected right now To use HubSpot tools on your WordPress site, %1$sconnect the plugin now%2$s'
|
||||
)
|
||||
.replace(
|
||||
'%1$s',
|
||||
'<a class="leadin-banner__link" href="admin.php?page=leadin&bannerClick=true">'
|
||||
)
|
||||
.replace('%2$s', '</a>'),
|
||||
}}
|
||||
></b>
|
||||
</ElementorBanner>
|
||||
);
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
import React from 'react';
|
||||
|
||||
interface IElementorBannerProps {
|
||||
type?: string;
|
||||
}
|
||||
|
||||
export default function ElementorBanner({
|
||||
type = 'warning',
|
||||
children,
|
||||
}: React.PropsWithChildren<IElementorBannerProps>) {
|
||||
return (
|
||||
<div className="elementor-control-content">
|
||||
<div
|
||||
className={`elementor-control-raw-html elementor-panel-alert elementor-panel-alert-${type}`}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
import { styled } from '@linaria/react';
|
||||
import React from 'react';
|
||||
|
||||
const Container = styled.div`
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
padding-bottom: 8px;
|
||||
`;
|
||||
|
||||
export default function ElementorButton({
|
||||
children,
|
||||
...params
|
||||
}: React.PropsWithChildren<React.ButtonHTMLAttributes<HTMLButtonElement>>) {
|
||||
return (
|
||||
<Container className="elementor-button-wrapper">
|
||||
<button
|
||||
className="elementor-button elementor-button-default"
|
||||
type="button"
|
||||
{...params}
|
||||
>
|
||||
{children}
|
||||
</button>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
import { styled } from '@linaria/react';
|
||||
|
||||
interface IElementorWrapperProps {
|
||||
pluginPath?: string;
|
||||
}
|
||||
|
||||
export default styled.div<IElementorWrapperProps>`
|
||||
background-image: ${props =>
|
||||
`url(${props.pluginPath}/plugin/assets/images/hubspot.svg)`};
|
||||
background-color: #f5f8fa;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center 25px;
|
||||
background-size: 120px;
|
||||
color: #33475b;
|
||||
font-family: 'Lexend Deca', Helvetica, Arial, sans-serif;
|
||||
font-size: 14px;
|
||||
|
||||
padding: ${(props: any) => props.padding || '90px 20% 25px'};
|
||||
|
||||
p {
|
||||
font-size: inherit !important;
|
||||
line-height: 24px;
|
||||
margin: 4px 0;
|
||||
}
|
||||
`;
|
||||
@@ -1,86 +0,0 @@
|
||||
import React, { useState, useEffect, Fragment } from 'react';
|
||||
import { portalId, refreshToken } from '../../constants/leadinConfig';
|
||||
import ElementorBanner from '../Common/ElementorBanner';
|
||||
import UISpinner from '../../shared/UIComponents/UISpinner';
|
||||
import { __ } from '@wordpress/i18n';
|
||||
import {
|
||||
BackgroudAppContext,
|
||||
useBackgroundAppContext,
|
||||
} from '../../iframe/useBackgroundApp';
|
||||
import useForms from './hooks/useForms';
|
||||
import { getOrCreateBackgroundApp } from '../../utils/backgroundAppUtils';
|
||||
|
||||
interface IElementorFormSelectProps {
|
||||
formId: string;
|
||||
setAttributes: Function;
|
||||
}
|
||||
|
||||
function ElementorFormSelect({
|
||||
formId,
|
||||
setAttributes,
|
||||
}: IElementorFormSelectProps) {
|
||||
const { hasError, forms, loading } = useForms();
|
||||
|
||||
return loading ? (
|
||||
<div>
|
||||
<UISpinner />
|
||||
</div>
|
||||
) : hasError ? (
|
||||
<ElementorBanner type="danger">
|
||||
{__('Please refresh your forms or try again in a few minutes', 'leadin')}
|
||||
</ElementorBanner>
|
||||
) : (
|
||||
<select
|
||||
value={formId}
|
||||
onChange={event => {
|
||||
const selectedForm = forms.find(
|
||||
form => form.value === event.target.value
|
||||
);
|
||||
if (selectedForm) {
|
||||
setAttributes({
|
||||
portalId,
|
||||
formId: selectedForm.value,
|
||||
formName: selectedForm.label,
|
||||
});
|
||||
}
|
||||
}}
|
||||
>
|
||||
<option value="" disabled={true} selected={true}>
|
||||
{__('Search for a form', 'leadin')}
|
||||
</option>
|
||||
{forms.map(form => (
|
||||
<option key={form.value} value={form.value}>
|
||||
{form.label}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
);
|
||||
}
|
||||
|
||||
function ElementorFormSelectWrapper(props: IElementorFormSelectProps) {
|
||||
const isBackgroundAppReady = useBackgroundAppContext();
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
{!isBackgroundAppReady ? (
|
||||
<div>
|
||||
<UISpinner />
|
||||
</div>
|
||||
) : (
|
||||
<ElementorFormSelect {...props} />
|
||||
)}
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
export default function ElementorFormSelectContainer(
|
||||
props: IElementorFormSelectProps
|
||||
) {
|
||||
return (
|
||||
<BackgroudAppContext.Provider
|
||||
value={refreshToken && getOrCreateBackgroundApp(refreshToken)}
|
||||
>
|
||||
<ElementorFormSelectWrapper {...props} />
|
||||
</BackgroudAppContext.Provider>
|
||||
);
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
import React, { Fragment } from 'react';
|
||||
import { connectionStatus } from '../../constants/leadinConfig';
|
||||
import ConnectPluginBanner from '../Common/ConnectPluginBanner';
|
||||
import ElementorFormSelect from './ElementorFormSelect';
|
||||
import { IFormAttributes } from './registerFormWidget';
|
||||
|
||||
const ConnectionStatus = {
|
||||
Connected: 'Connected',
|
||||
NotConnected: 'NotConnected',
|
||||
};
|
||||
|
||||
export default function FormControlController(
|
||||
attributes: IFormAttributes,
|
||||
setValue: Function
|
||||
) {
|
||||
return () => {
|
||||
const render = () => {
|
||||
if (connectionStatus === ConnectionStatus.Connected) {
|
||||
return (
|
||||
<ElementorFormSelect
|
||||
formId={attributes.formId}
|
||||
setAttributes={setValue}
|
||||
/>
|
||||
);
|
||||
} else {
|
||||
return <ConnectPluginBanner />;
|
||||
}
|
||||
};
|
||||
return <Fragment>{render()}</Fragment>;
|
||||
};
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
import React, { Fragment } from 'react';
|
||||
import { connectionStatus } from '../../constants/leadinConfig';
|
||||
import ErrorHandler from '../../shared/Common/ErrorHandler';
|
||||
import FormEdit from '../../shared/Form/FormEdit';
|
||||
import ConnectionStatus from '../../shared/enums/connectionStatus';
|
||||
import { IFormAttributes } from './registerFormWidget';
|
||||
|
||||
export default function FormWidgetController(
|
||||
attributes: IFormAttributes,
|
||||
setValue: Function
|
||||
) {
|
||||
return () => {
|
||||
const render = () => {
|
||||
if (connectionStatus === ConnectionStatus.Connected) {
|
||||
return (
|
||||
<FormEdit
|
||||
attributes={attributes}
|
||||
isSelected={true}
|
||||
setAttributes={setValue}
|
||||
preview={false}
|
||||
origin="elementor"
|
||||
/>
|
||||
);
|
||||
} else {
|
||||
return <ErrorHandler status={401} />;
|
||||
}
|
||||
};
|
||||
return <Fragment>{render()}</Fragment>;
|
||||
};
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import LoadState, { LoadStateType } from '../../../shared/enums/loadState';
|
||||
import { ProxyMessages } from '../../../iframe/integratedMessages';
|
||||
import { usePostAsyncBackgroundMessage } from '../../../iframe/useBackgroundApp';
|
||||
import { IForm } from '../../../shared/types';
|
||||
|
||||
interface FormOption {
|
||||
label: string;
|
||||
value: string;
|
||||
}
|
||||
|
||||
export default function useForms() {
|
||||
const proxy = usePostAsyncBackgroundMessage();
|
||||
const [loadState, setLoadState] = useState<LoadStateType>(
|
||||
LoadState.NotLoaded
|
||||
);
|
||||
const [hasError, setError] = useState(null);
|
||||
const [forms, setForms] = useState<FormOption[]>([]);
|
||||
|
||||
useEffect(() => {
|
||||
if (loadState === LoadState.NotLoaded) {
|
||||
proxy({
|
||||
key: ProxyMessages.FetchForms,
|
||||
payload: {
|
||||
search: '',
|
||||
},
|
||||
})
|
||||
.then(data => {
|
||||
setForms(
|
||||
data.map((form: IForm) => ({
|
||||
label: form.name,
|
||||
value: form.guid,
|
||||
}))
|
||||
);
|
||||
setLoadState(LoadState.Loaded);
|
||||
})
|
||||
.catch(error => {
|
||||
setError(error);
|
||||
setLoadState(LoadState.Failed);
|
||||
});
|
||||
}
|
||||
}, [loadState]);
|
||||
|
||||
return { forms, loading: loadState === LoadState.Loading, hasError };
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
import ReactDOM from 'react-dom';
|
||||
import FormControlController from './FormControlController';
|
||||
import FormWidgetController from './FormWidgetController';
|
||||
|
||||
export interface IFormAttributes {
|
||||
formId: string;
|
||||
formName: string;
|
||||
portalId: string;
|
||||
}
|
||||
|
||||
export default class registerFormWidget {
|
||||
widgetContainer: Element;
|
||||
attributes: IFormAttributes;
|
||||
controlContainer: Element;
|
||||
setValue: Function;
|
||||
|
||||
constructor(controlContainer: any, widgetContainer: any, setValue: Function) {
|
||||
const attributes = widgetContainer.dataset.attributes
|
||||
? JSON.parse(widgetContainer.dataset.attributes)
|
||||
: {};
|
||||
|
||||
this.widgetContainer = widgetContainer;
|
||||
this.controlContainer = controlContainer;
|
||||
this.setValue = setValue;
|
||||
this.attributes = attributes;
|
||||
}
|
||||
|
||||
render() {
|
||||
ReactDOM.render(
|
||||
FormWidgetController(this.attributes, this.setValue)(),
|
||||
this.widgetContainer
|
||||
);
|
||||
|
||||
ReactDOM.render(
|
||||
FormControlController(this.attributes, this.setValue)(),
|
||||
this.controlContainer
|
||||
);
|
||||
}
|
||||
|
||||
done() {
|
||||
ReactDOM.unmountComponentAtNode(this.widgetContainer);
|
||||
ReactDOM.unmountComponentAtNode(this.controlContainer);
|
||||
}
|
||||
}
|
||||
@@ -1,122 +0,0 @@
|
||||
import React, { Fragment, useState } from 'react';
|
||||
import ElementorBanner from '../Common/ElementorBanner';
|
||||
import UISpinner from '../../shared/UIComponents/UISpinner';
|
||||
import ElementorMeetingWarning from './ElementorMeetingWarning';
|
||||
import useMeetings, {
|
||||
useSelectedMeetingCalendar,
|
||||
} from '../../shared/Meeting/hooks/useMeetings';
|
||||
import { __ } from '@wordpress/i18n';
|
||||
import Raven from 'raven-js';
|
||||
import {
|
||||
BackgroudAppContext,
|
||||
useBackgroundAppContext,
|
||||
} from '../../iframe/useBackgroundApp';
|
||||
import { refreshToken } from '../../constants/leadinConfig';
|
||||
import { getOrCreateBackgroundApp } from '../../utils/backgroundAppUtils';
|
||||
|
||||
interface IElementorMeetingSelectProps {
|
||||
url: string;
|
||||
setAttributes: Function;
|
||||
}
|
||||
|
||||
function ElementorMeetingSelect({
|
||||
url,
|
||||
setAttributes,
|
||||
}: IElementorMeetingSelectProps) {
|
||||
const {
|
||||
mappedMeetings: meetings,
|
||||
loading,
|
||||
error,
|
||||
reload,
|
||||
connectCalendar,
|
||||
} = useMeetings();
|
||||
const selectedMeetingCalendar = useSelectedMeetingCalendar(url);
|
||||
const [localUrl, setLocalUrl] = useState(url);
|
||||
|
||||
const handleConnectCalendar = () => {
|
||||
return connectCalendar()
|
||||
.then(() => {
|
||||
reload();
|
||||
})
|
||||
.catch(error => {
|
||||
Raven.captureMessage('Unable to connect calendar', {
|
||||
extra: { error },
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
{loading ? (
|
||||
<div>
|
||||
<UISpinner />
|
||||
</div>
|
||||
) : error ? (
|
||||
<ElementorBanner type="danger">
|
||||
{__(
|
||||
'Please refresh your meetings or try again in a few minutes',
|
||||
'leadin'
|
||||
)}
|
||||
</ElementorBanner>
|
||||
) : (
|
||||
<Fragment>
|
||||
{selectedMeetingCalendar && (
|
||||
<ElementorMeetingWarning
|
||||
status={selectedMeetingCalendar}
|
||||
onConnectCalendar={connectCalendar}
|
||||
/>
|
||||
)}
|
||||
{meetings.length > 1 && (
|
||||
<select
|
||||
value={localUrl}
|
||||
onChange={event => {
|
||||
const newUrl = event.target.value;
|
||||
setLocalUrl(newUrl);
|
||||
setAttributes({
|
||||
url: newUrl,
|
||||
});
|
||||
}}
|
||||
>
|
||||
<option value="" disabled={true} selected={true}>
|
||||
{__('Select a meeting', 'leadin')}
|
||||
</option>
|
||||
{meetings.map(item => (
|
||||
<option key={item.value} value={item.value}>
|
||||
{item.label}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
)}
|
||||
</Fragment>
|
||||
)}
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
function ElementorMeetingSelectWrapper(props: IElementorMeetingSelectProps) {
|
||||
const isBackgroundAppReady = useBackgroundAppContext();
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
{!isBackgroundAppReady ? (
|
||||
<div>
|
||||
<UISpinner />
|
||||
</div>
|
||||
) : (
|
||||
<ElementorMeetingSelect {...props} />
|
||||
)}
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
export default function ElementorMeetingsSelectContainer(
|
||||
props: IElementorMeetingSelectProps
|
||||
) {
|
||||
return (
|
||||
<BackgroudAppContext.Provider
|
||||
value={refreshToken && getOrCreateBackgroundApp(refreshToken)}
|
||||
>
|
||||
<ElementorMeetingSelectWrapper {...props} />
|
||||
</BackgroudAppContext.Provider>
|
||||
);
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
import React, { Fragment } from 'react';
|
||||
import { CURRENT_USER_CALENDAR_MISSING } from '../../shared/Meeting/constants';
|
||||
import ElementorButton from '../Common/ElementorButton';
|
||||
import ElementorBanner from '../Common/ElementorBanner';
|
||||
import { styled } from '@linaria/react';
|
||||
import { __ } from '@wordpress/i18n';
|
||||
|
||||
const Container = styled.div`
|
||||
padding-bottom: 8px;
|
||||
`;
|
||||
|
||||
interface IMeetingWarningPros {
|
||||
onConnectCalendar: React.MouseEventHandler<HTMLButtonElement>;
|
||||
status: string;
|
||||
}
|
||||
|
||||
export default function MeetingWarning({
|
||||
onConnectCalendar,
|
||||
status,
|
||||
}: IMeetingWarningPros) {
|
||||
const isMeetingOwner = status === CURRENT_USER_CALENDAR_MISSING;
|
||||
const titleText = isMeetingOwner
|
||||
? __('Your calendar is not connected', 'leadin')
|
||||
: __('Calendar is not connected', 'leadin');
|
||||
const titleMessage = isMeetingOwner
|
||||
? __(
|
||||
'Please connect your calendar to activate your scheduling pages',
|
||||
'leadin'
|
||||
)
|
||||
: __(
|
||||
'Make sure that everybody in this meeting has connected their calendar from the Meetings page in HubSpot',
|
||||
'leadin'
|
||||
);
|
||||
return (
|
||||
<Fragment>
|
||||
<Container>
|
||||
<ElementorBanner type="warning">
|
||||
<b>{titleText}</b>
|
||||
<br />
|
||||
{titleMessage}
|
||||
</ElementorBanner>
|
||||
</Container>
|
||||
{isMeetingOwner && (
|
||||
<ElementorButton
|
||||
id="meetings-connect-calendar"
|
||||
onClick={onConnectCalendar}
|
||||
>
|
||||
{__('Connect calendar', 'leadin')}
|
||||
</ElementorButton>
|
||||
)}
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
import React, { Fragment } from 'react';
|
||||
import { connectionStatus } from '../../constants/leadinConfig';
|
||||
import ConnectPluginBanner from '../Common/ConnectPluginBanner';
|
||||
import ElementorMeetingSelect from './ElementorMeetingSelect';
|
||||
import { IMeetingAttributes } from './registerMeetingWidget';
|
||||
|
||||
const ConnectionStatus = {
|
||||
Connected: 'Connected',
|
||||
NotConnected: 'NotConnected',
|
||||
};
|
||||
|
||||
export default function MeetingControlController(
|
||||
attributes: IMeetingAttributes,
|
||||
setValue: Function
|
||||
) {
|
||||
return () => {
|
||||
const render = () => {
|
||||
if (connectionStatus === ConnectionStatus.Connected) {
|
||||
return (
|
||||
<ElementorMeetingSelect
|
||||
url={attributes.url}
|
||||
setAttributes={setValue}
|
||||
/>
|
||||
);
|
||||
} else {
|
||||
return <ConnectPluginBanner />;
|
||||
}
|
||||
};
|
||||
return <Fragment>{render()}</Fragment>;
|
||||
};
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
import React, { Fragment } from 'react';
|
||||
import { connectionStatus } from '../../constants/leadinConfig';
|
||||
import ErrorHandler from '../../shared/Common/ErrorHandler';
|
||||
import MeetingsEdit from '../../shared/Meeting/MeetingEdit';
|
||||
import { IMeetingAttributes } from './registerMeetingWidget';
|
||||
|
||||
const ConnectionStatus = {
|
||||
Connected: 'Connected',
|
||||
NotConnected: 'NotConnected',
|
||||
};
|
||||
|
||||
export default function MeetingWidgetController(
|
||||
attributes: IMeetingAttributes,
|
||||
setValue: Function
|
||||
) {
|
||||
return () => {
|
||||
const render = () => {
|
||||
if (connectionStatus === ConnectionStatus.Connected) {
|
||||
return (
|
||||
<MeetingsEdit
|
||||
attributes={attributes}
|
||||
isSelected={true}
|
||||
setAttributes={setValue}
|
||||
preview={false}
|
||||
origin="elementor"
|
||||
/>
|
||||
);
|
||||
} else {
|
||||
return <ErrorHandler status={401} />;
|
||||
}
|
||||
};
|
||||
return <Fragment>{render()}</Fragment>;
|
||||
};
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
import ReactDOM from 'react-dom';
|
||||
import MeetingControlController from './MeetingControlController';
|
||||
import MeetingWidgetController from './MeetingWidgetController';
|
||||
|
||||
export interface IMeetingAttributes {
|
||||
url: string;
|
||||
}
|
||||
|
||||
export default class registerMeetingsWidget {
|
||||
widgetContainer: Element;
|
||||
controlContainer: Element;
|
||||
setValue: Function;
|
||||
attributes: IMeetingAttributes;
|
||||
|
||||
constructor(controlContainer: any, widgetContainer: any, setValue: Function) {
|
||||
const attributes = widgetContainer.dataset.attributes
|
||||
? JSON.parse(widgetContainer.dataset.attributes)
|
||||
: {};
|
||||
|
||||
this.widgetContainer = widgetContainer;
|
||||
this.controlContainer = controlContainer;
|
||||
this.setValue = setValue;
|
||||
this.attributes = attributes;
|
||||
}
|
||||
|
||||
render() {
|
||||
ReactDOM.render(
|
||||
MeetingWidgetController(this.attributes, this.setValue)(),
|
||||
this.widgetContainer
|
||||
);
|
||||
|
||||
ReactDOM.render(
|
||||
MeetingControlController(this.attributes, this.setValue)(),
|
||||
this.controlContainer
|
||||
);
|
||||
}
|
||||
|
||||
done() {
|
||||
ReactDOM.unmountComponentAtNode(this.widgetContainer);
|
||||
ReactDOM.unmountComponentAtNode(this.controlContainer);
|
||||
}
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
export default function elementorWidget(
|
||||
elementor: any,
|
||||
options: any,
|
||||
callback: Function,
|
||||
done = () => {}
|
||||
) {
|
||||
return elementor.modules.controls.BaseData.extend({
|
||||
onReady() {
|
||||
const self = this;
|
||||
const controlContainer = this.ui.contentEditable.prevObject[0].querySelector(
|
||||
options.controlSelector
|
||||
);
|
||||
let widgetContainer = this.options.element.$el[0].querySelector(
|
||||
options.containerSelector
|
||||
);
|
||||
if (widgetContainer) {
|
||||
callback(controlContainer, widgetContainer, (args: any) =>
|
||||
self.setValue(args)
|
||||
);
|
||||
} else {
|
||||
//@ts-expect-error global
|
||||
window.elementorFrontend.hooks.addAction(
|
||||
`frontend/element_ready/${options.widgetName}.default`,
|
||||
(element: HTMLElement[]) => {
|
||||
widgetContainer = element[0].querySelector(
|
||||
options.containerSelector
|
||||
);
|
||||
callback(controlContainer, widgetContainer, (args: any) =>
|
||||
self.setValue(args)
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
},
|
||||
saveValue(props: any) {
|
||||
this.setValue(props);
|
||||
},
|
||||
onBeforeDestroy() {
|
||||
//@ts-expect-error global
|
||||
window.elementorFrontend.hooks.removeAction(
|
||||
`frontend/element_ready/${options.widgetName}.default`
|
||||
);
|
||||
done();
|
||||
},
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user