plugin updates

This commit is contained in:
Tony Volpe
2024-06-17 15:33:26 -04:00
parent 3751a5a1a6
commit e4e274a9a7
2674 changed files with 0 additions and 507851 deletions

View File

@@ -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>
);
}

View File

@@ -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>
);
}

View File

@@ -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>
);
}

View File

@@ -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;
}
`;

View File

@@ -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>
);
}

View File

@@ -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>;
};
}

View File

@@ -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>;
};
}

View File

@@ -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 };
}

View File

@@ -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);
}
}

View File

@@ -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>
);
}

View File

@@ -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>
);
}

View File

@@ -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>;
};
}

View File

@@ -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>;
};
}

View File

@@ -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);
}
}

View File

@@ -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();
},
});
}