Commit inicial - WordPress Análisis de Precios Unitarios

- WordPress core y plugins
- Tema Twenty Twenty-Four configurado
- Plugin allow-unfiltered-html.php simplificado
- .gitignore configurado para excluir wp-config.php y uploads

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
root
2025-11-03 21:04:30 -06:00
commit a22573bf0b
24068 changed files with 4993111 additions and 0 deletions

View File

@@ -0,0 +1,130 @@
/**
* External Dependencies
*/
import { useState } from '@wordpress/element';
/**
* Internal Dependencies
*/
import Header from './Header';
import Footer from './Footer';
import StepWrapper from './StepWrapper';
import { Wizard } from '@components/wizard';
import Step1 from './steps/Step1';
import GoogleAdsense from './steps/GoogleAdsense';
import BannerAd from './steps/BannerAd';
import CodeAd from './steps/CodeAd';
import Congrats from './steps/Congrats';
let initDone = false;
function initialState() {
const initial = {
startIndex: 1,
taskOption: 'google_adsense',
googleAdsPlacement: 'manual',
autoAdsOptions: [],
adsenseData: false,
};
const params = new URLSearchParams(document.location.search);
const wizardData = advancedAds.wizard.adsenseData;
if (
'adsense' === params.get('route') &&
'advanced-ads-onboarding' === params.get('page')
) {
initial.startIndex = 2;
initial.taskOption = 'google_adsense';
}
if (
'image' === params.get('route') &&
'advanced-ads-onboarding' === params.get('page')
) {
initial.startIndex = 2;
initial.taskOption = 'ad_image';
}
if (!Array.isArray(wizardData.accounts)) {
const adsenseId = wizardData['adsense-id'];
initial.adsenseData = {
account: {
id: adsenseId,
name: wizardData.accounts[adsenseId].details.name,
},
};
}
if (!initDone) {
if (wizardData.amp) {
initial.autoAdsOptions.push('enableAmp');
}
if (wizardData['page-level-enabled']) {
initial.autoAdsOptions.push('enable');
}
initDone = true;
}
return initial;
}
export default function App() {
const [options, setOptions] = useState({
...initialState(),
});
const handleOptions = (key, value) => {
const newOptions = {
...options,
[key]: value,
};
setOptions(newOptions);
};
return (
<div className="advads-onboarding advads-onboarding-frame">
<div className="absolute top-0 max-w-3xl w-full py-20">
<Wizard
header={<Header />}
footer={<Footer />}
startIndex={options.startIndex}
>
<StepWrapper>
<Step1 options={options} setOptions={handleOptions} />
</StepWrapper>
{'google_adsense' === options.taskOption && (
<StepWrapper className="pb-4">
<GoogleAdsense
options={options}
setOptions={handleOptions}
/>
</StepWrapper>
)}
{'ad_image' === options.taskOption && (
<StepWrapper className="pb-4">
<BannerAd
options={options}
setOptions={handleOptions}
/>
</StepWrapper>
)}
{'ad_code' === options.taskOption && (
<StepWrapper className="pb-4">
<CodeAd
options={options}
setOptions={handleOptions}
/>
</StepWrapper>
)}
<StepWrapper>
<Congrats
options={options}
setOptions={handleOptions}
/>
</StepWrapper>
</Wizard>
</div>
</div>
);
}

View File

@@ -0,0 +1,29 @@
/**
* External Dependencies
*/
import { wizard } from '@advancedAds/i18n';
/**
* Internal Dependencies
*/
import { adminUrl } from '@utilities';
import { useWizard } from '@components/wizard';
export default function Footer() {
const { isLastStep } = useWizard();
if (isLastStep) {
return null;
}
return (
<div className="text-center">
<a
href={adminUrl('admin.php?page=advanced-ads')}
className="no-underline text-base border-0 border-b-2 pb-0.5 border-solid border-gray-800 text-gray-800"
>
{wizard.exitLabel}
</a>
</div>
);
}

View File

@@ -0,0 +1,43 @@
/**
* External Dependencies
*/
import clx from 'classnames';
/**
* Internal Dependencies
*/
import { useWizard } from '@components/wizard';
import { assetsUrl } from '@utilities';
export default function Header() {
const { stepCount, activeStep } = useWizard();
const steps = Array.apply(null, { length: stepCount });
return (
<div className="text-center">
<img
className="w-8/12 m-auto"
src={assetsUrl('assets/img/advancedads-full-logo.svg')}
alt=""
/>
<div className="advads-wizard-progress">
{steps.map((step, index) => {
const isActive = activeStep === index;
const stepClassName = clx(
'advads-wizard-progress--item',
activeStep > index ? 'is-done' : '',
isActive ? 'is-active' : ''
);
return (
<div className={stepClassName} key={`step-${index}`}>
<div className="advads-wizard-progress--count">
{isActive ? `Step ${index + 1}` : index + 1}
</div>
</div>
);
})}
</div>
</div>
);
}

View File

@@ -0,0 +1,57 @@
/**
* External Dependencies
*/
import { wizard } from '@advancedAds/i18n';
/**
* Internal Dependencies
*/
import { useWizard } from '@components/wizard';
import Divider from '@components/Divider';
export default function StepFooter({
isEnabled,
enableText,
disableText,
onNext,
}) {
const { previousStep, nextStep } = useWizard();
const handleNext = async () => {
if (onNext) {
onNext();
}
nextStep();
};
return (
<>
<Divider className="mb-4" />
<div className="flex items-center justify-between">
<div>
<button
onClick={previousStep}
className="button-onboarding !bg-white !border-gray-100 !text-gray-600"
>
{wizard.btnGoBack}
</button>
</div>
<div>
{isEnabled ? (
<button
className="button-onboarding"
onClick={handleNext}
>
{enableText}
</button>
) : (
<button className="button-onboarding" disabled={true}>
{disableText}
</button>
)}
</div>
</div>
</>
);
}

View File

@@ -0,0 +1,9 @@
export default function StepWrapper({ children, className = '' }) {
return (
<div
className={`bg-white mt-4 mb-8 p-8 border-solid border-gray-200 rounded-sm ${className}`}
>
{children}
</div>
);
}

View File

@@ -0,0 +1,25 @@
/**
* WordPress Dependencies
*/
import domReady from '@wordpress/dom-ready';
import { createRoot, createElement, render } from '@wordpress/element';
/**
* Internal Dependencies
*/
import App from './App';
/**
* Init
*/
domReady(() => {
// Render App.
const domElement = document.getElementById('advads-onboarding-wizard');
const uiElement = createElement(App);
if (createRoot) {
createRoot(domElement).render(uiElement);
} else {
render(uiElement, domElement);
}
});

View File

@@ -0,0 +1,110 @@
/**
* External Dependencies
*/
import { wizard } from '@advancedAds/i18n';
import { useEffect } from '@wordpress/element';
/**
* Internal Dependencies
*/
import StepFooter from '../StepFooter';
export default function BannerAd({ options, setOptions }) {
let fileFrame = null;
const handleUpload = (event) => {
event.preventDefault();
if (fileFrame) {
fileFrame.uploader.uploader.param('post_id', 0);
fileFrame.open();
return;
}
fileFrame = wp.media.frames.file_frame = wp.media({
title: wizard.bannerAd.mediaFrameTitle,
button: {
text: wizard.bannerAd.mediaFrameButton,
},
multiple: false,
});
fileFrame.on('select', () => {
const attachment = fileFrame
.state()
.get('selection')
.first()
.toJSON();
setOptions('adImage', attachment);
});
fileFrame.open();
};
useEffect(() => {
if (options.adImage) {
const fileName = options.adImage.url.split('/').pop();
const maxWidth = 250;
const textP = document.createElement('p');
textP.style.visibility = 'hidden';
textP.style.whiteSpace = 'nowrap';
textP.style.position = 'absolute';
textP.innerText = fileName;
document.body.appendChild(textP);
const isTextLong = textP.offsetWidth > maxWidth;
document.body.removeChild(textP);
setOptions('isTextLong', isTextLong);
}
}, [options.adImage]);
return (
<>
<h1 className={`!mt-0 ${options.adImage ? 'text-gray-300' : ''}`}>
{wizard.stepTitles.adImage}
</h1>
{options.adImage ? (
<div className="space-y-4">
<div className="flex items-center gap-5">
<button
className="button-onboarding button-onboarding--gray"
onClick={handleUpload}
>
{wizard.bannerAd.mediaBtnReplace}
</button>
<div
className={`file-name-rtl m-0 ${options.isTextLong ? 'truncate' : ''}`}
>
<p>{options.adImage.url.split('/').pop()}</p>
</div>
<span className="dashicons dashicons-yes-alt flex items-center justify-center text-4xl w-9 h-9 text-primary"></span>
</div>
<div>
<h2>{wizard.bannerAd.stepHeading}</h2>
<input
type="url"
name="ad_image_url"
id="ad_image_url"
className="advads-input-text"
placeholder={wizard.bannerAd.inputPlaceholder}
onChange={(event) =>
setOptions('adImageUrl', event.target.value)
}
/>
</div>
</div>
) : (
<button className="button-onboarding" onClick={handleUpload}>
{wizard.bannerAd.mediaBtnUpload}
</button>
)}
<StepFooter
isEnabled={options.adImage}
enableText={wizard.bannerAd.footerEnableText}
disableText={wizard.bannerAd.footerDisableText}
onNext={() => {}}
/>
</>
);
}

View File

@@ -0,0 +1,39 @@
/**
* External Dependencies
*/
import { wizard } from '@advancedAds/i18n';
/**
* Internal Dependencies
*/
import StepFooter from '../StepFooter';
export default function CodeAd({ options, setOptions }) {
return (
<>
<h1 className={`!mt-0 ${options.adCode ? 'text-gray-300' : ''}`}>
{wizard.stepTitles.adCode}
</h1>
<div className="space-y-4">
<div>
<textarea
name="ad_code_code"
id="ad_code_code"
className="w-full p-4 text-base"
rows={6}
placeholder={wizard.codeAd.inputPlaceholder}
onChange={(event) =>
setOptions('adCode', event.target.value)
}
/>
</div>
</div>
<StepFooter
isEnabled={options.adCode}
enableText={wizard.codeAd.footerEnableText}
disableText={wizard.codeAd.footerDisableText}
onNext={() => {}}
/>
</>
);
}

View File

@@ -0,0 +1,269 @@
/**
* External Dependencies
*/
import { useState } from '@wordpress/element';
import apiFetch from '@wordpress/api-fetch';
import { wizard } from '@advancedAds/i18n';
/**
* Internal Dependencies
*/
import { adminUrl } from '@utilities';
import Divider from '@components/Divider';
import Checkmark from '../../icons/Checkmark';
import Upgradebox from '../../icons/UpgradeBox';
function ListItem({ title = '', text, icon = false }) {
return (
<div className="flex w-full gap-x-3 items-center">
{icon && (
<div className="mt-2">
<Checkmark />
</div>
)}
<div className={`grow ${icon ? '' : 'ml-7'}`}>
<strong>{title}</strong> {text}
</div>
</div>
);
}
function Newsletter({ email, setEmail }) {
return (
<>
<h1 className="!mt-0">{wizard.newsletter.title}</h1>
<div className="advads-admin-notice">
<div className="advads-notice-box_wrapper flex gap-7 items-center">
<input
type="email"
id="newsletter_email"
className="advads-input-text"
placeholder={wizard.newsletter.inputPlaceholder}
value={email}
onChange={(e) => setEmail(e.target.value)}
style={{ minWidth: '65%', maxWidth: '65%' }}
/>
<div>
<button
className="button-onboarding advads-notices-button-subscribe"
data-notice="nl_free_addons"
>
{wizard.newsletter.btnLabel}
</button>
</div>
</div>
</div>
</>
);
}
function Loader() {
return (
/* eslint-disable jsx-a11y/anchor-is-valid */
<a
href="#"
className="button-onboarding button-onboarding--gray disabled"
>
{wizard.loading}
</a>
/* eslint-enable jsx-a11y/anchor-is-valid */
);
}
function CongratsHeader(options, result) {
let title, para;
title = wizard.stepTitles.congrats.default;
para = wizard.congrats.stepHeading;
if (options.taskOption === 'google_adsense') {
title =
options.googleAdsPlacement === 'auto_ads'
? wizard.stepTitles.congrats.adsenseAuto
: wizard.stepTitles.congrats.adsenseManual;
para =
options.googleAdsPlacement === 'auto_ads'
? wizard.congrats.adsenseAuto.stepHeading
: wizard.congrats.adsenseManual.stepHeading;
}
return (
<>
<h1 className="!mt-0">{title}</h1>
<div className="congrats-flex">
<p
className="text-justify min-w m-0"
dangerouslySetInnerHTML={{
__html: para,
}}
/>
{getEditButton(options, result)}
</div>
{getLiveHeading(options, result)}
</>
);
}
function getEditButton(options, result) {
if (options.taskOption === 'google_adsense' && result?.success) {
if (
options.googleAdsPlacement === 'auto_ads' &&
result.adsenseAccount
) {
return (
<div>
<a
href={result.adsenseAccount}
className="button-onboarding button-onboarding--gray"
target="_blank"
rel="noreferrer"
>
{wizard.congrats.adsenseAuto.btnAccount}
</a>
</div>
);
}
if (options.googleAdsPlacement === 'manual' && result.itemEditLink) {
return (
<div>
<a
href={result.itemEditLink}
className="button-onboarding !bg-red-600 !border-red-700 !text-white"
>
{wizard.congrats.adsenseManual.btnEditItem}
</a>
</div>
);
}
}
if (result?.success && result.itemEditLink) {
return (
<div>
<a
href={result.itemEditLink}
className="button-onboarding button-onboarding--gray"
>
{wizard.congrats.btnEditItem}
</a>
</div>
);
}
return null;
}
function getLiveHeading(options, result) {
if (
options.taskOption === 'google_adsense' &&
options.googleAdsPlacement === 'auto_ads'
) {
return null;
}
if (
options.taskOption === 'google_adsense' &&
options.googleAdsPlacement === 'manual'
) {
return (
<div className="congrats-flex mt-7">
<p
className="m-0"
dangerouslySetInnerHTML={{
__html: wizard.congrats.adsenseManual.liveHeading,
}}
/>
</div>
);
}
return (
<div className="congrats-flex mt-7">
<p
className="m-0"
dangerouslySetInnerHTML={{
__html: wizard.congrats.liveHeading,
}}
/>
<div className="mr-3">
{result && result.success && '' !== result.postLink ? (
<a href={result.postLink} className="button-onboarding">
{wizard.congrats.btnLiveAd}
</a>
) : (
<Loader />
)}
</div>
</div>
);
}
export default function Congrats({ options }) {
const [result, setResult] = useState(null);
const [userEmail, setUserEmail] = useState('');
if (result === null) {
apiFetch({
path: '/advanced-ads/v1/onboarding',
method: 'POST',
data: options,
}).then((response) => {
setResult(response);
});
}
if (userEmail === '') {
apiFetch({
path: '/advanced-ads/v1/user-email',
method: 'GET',
}).then((email) => {
setUserEmail(email);
});
}
return (
<>
{CongratsHeader(options, result)}
<Divider />
<Newsletter email={userEmail} setEmail={setUserEmail} />
<Divider />
<h1 className="!mt-0">{wizard.congrats.upgradeHeading}</h1>
<div className="congrats-flex items-center gap-x-12">
<p className="text-justify m-0">
{wizard.congrats.upgradeText}
</p>
<a
href="https://wpadvancedads.com/add-ons/all-access/?utm_source=advanced-ads&utm_medium=link&utm_campaign=wizard-upgrade"
className="button-onboarding !bg-red-600 !border-red-700 !text-white text-center"
target="_blank"
rel="noreferrer"
>
{wizard.congrats.btnUpgrade}
</a>
</div>
<div className="flex gap-x-12 items-center">
<div className="space-y-2 mt-4 text-lg tracking-wide grow">
{wizard.congrats.upgradePoints.map((point, index) => (
<ListItem key={`point-${index}`} {...point} />
))}
</div>
<div>
<Upgradebox className="w-40" />
</div>
</div>
<Divider />
<div className="text-right">
<a
href={adminUrl('admin.php?page=advanced-ads')}
className="button-onboarding button-onboarding--gray"
>
{wizard.congrats.btnDashboard}
</a>
</div>
</>
);
}

View File

@@ -0,0 +1,209 @@
/**
* External Dependencies
*/
import { wizard } from '@advancedAds/i18n';
/**
* Internal Dependencies
*/
import RadioList from '@components/inputs/RadioList';
import CheckboxList from '@components/inputs/CheckboxList';
import ErrorMessage from '@components/ErrorMessage';
import {
authUrl,
hasAuthCode,
submitCode,
getAccountDetails,
getErrorMessage,
} from '../../utilities';
import StepFooter from '../StepFooter';
import Preloader from '../../components/Preloader';
import SelectAccount from '../../components/SelectAccount';
export default function GoogleAdsense({ options, setOptions }) {
if (!options.adsenseData.account) {
if (options.adsenseData.phase) {
switch (options.adsenseData.phase) {
case 'error':
// Something went south, abort.
return <ErrorMessage message={options.adsenseData.error} />;
case 'select':
// Ask the user to choose one account from the network account.
return (
<SelectAccount
accounts={options.adsenseData.accountsList}
tokenData={options.adsenseData.tokenData}
done={(response) => {
setOptions('adsenseData', {
account: response.account,
});
}}
fail={(response) => {
let message = getErrorMessage(response);
if (!message) {
message = wizard.googleAd.errors.notSaved;
}
setOptions('adsenseData', {
phase: 'error',
error: message,
});
}}
/>
);
default:
}
}
if (hasAuthCode()) {
// Submit the authorization code from Google's oAuth2 page.
submitCode()
.done(function (response) {
// Got the refresh token, get account info.
getAccountDetails(response.token_data)
.done(function (accDetails) {
if (accDetails.account) {
// Standard account.
setOptions('adsenseData', {
account: accDetails.account,
});
}
if (accDetails.details) {
// Network account, show the list of child accounts to choose from.
setOptions('adsenseData', {
phase: 'select',
accountsList: accDetails.details,
tokenData: accDetails.token_data,
});
}
})
.fail(function (detailsRes) {
// Error while getting account details.
let message = getErrorMessage(detailsRes);
if (!message) {
message = wizard.googleAd.errors.notFetched;
}
setOptions('adsenseData', {
phase: 'error',
error: message,
});
});
})
.fail(function (response) {
// Error while requesting the refresh (permanent) token.
let message = getErrorMessage(response);
if (!message) {
message = wizard.googleAd.errors.notAuthorized;
}
setOptions('adsenseData', {
phase: 'error',
error: message,
});
});
return <Preloader />;
}
return (
<>
<h1 className="!mt-0">{wizard.googleAd.stepHeading}</h1>
<div className="mt-8 flex gap-x-8 justify-start items-center font-medium">
<a
className="button button-hero !text-base !px-3 !py-4"
href={advancedAds.wizard.newAccountLink}
target="_blank"
rel="noopener noreferrer"
>
{wizard.googleAd.btnSignup}
</a>
<a
className="button-onboarding !text-base !px-3 !py-4"
href={authUrl()}
>
{wizard.googleAd.btnConnect}
</a>
</div>
<StepFooter
isEnabled={false}
enableText={wizard.googleAd.footerProcessText}
disableText={wizard.googleAd.footerDisableText}
/>
</>
);
}
const enableText = (() => {
switch (options.googleAdsPlacement) {
case 'auto_ads':
return wizard.googleAd.footerEnableText.autoAds;
case 'manual':
return wizard.googleAd.footerEnableText.manual;
default:
return wizard.googleAd.footerDisableText;
}
})();
return (
<>
<h1 className="!mt-0 !text-gray-300">
{wizard.googleAd.stepHeading}
</h1>
<div className="space-y-4">
<p>
{wizard.googleAd.labelConnected}{' '}
{options.adsenseData.account.id}
<br />
{wizard.googleAd.labelAccount}{' '}
{options.adsenseData.account.name}
</p>
<div>
<h2
className={
'auto_ads' === options.googleAdsPlacement
? '!text-gray-300'
: null
}
>
{wizard.googleAd.labelAdsPlacement}
</h2>
<RadioList
id="google_ads_placement"
options={wizard.googleAd.adsPlacement}
value={options.googleAdsPlacement}
onChange={(value) =>
setOptions('googleAdsPlacement', value)
}
/>
</div>
{options.googleAdsPlacement &&
'auto_ads' === options.googleAdsPlacement && (
<div>
<h2>{wizard.googleAd.labelAutoAds}</h2>
<CheckboxList
id="auto_ads"
options={wizard.googleAd.autoAdsOptions}
value={options.autoAdsOptions}
onChange={(value) => {
const newOptions = [
...options.autoAdsOptions,
];
const index = newOptions.indexOf(value);
if (index > -1) {
newOptions.splice(index, 1);
} else {
newOptions.push(value);
}
setOptions('autoAdsOptions', newOptions);
}}
/>
</div>
)}
</div>
<StepFooter
isEnabled={options.googleAdsPlacement}
enableText={enableText}
disableText={wizard.googleAd.footerDisableText}
/>
</>
);
}

View File

@@ -0,0 +1,81 @@
/**
* External Dependencies
*/
import { wizard } from '@advancedAds/i18n';
/**
* Internal Dependencies
*/
import { useWizard } from '@components/wizard';
import RadioList from '@components/inputs/RadioList';
import IconAdSense from '../../icons/AdSense';
import IconCode from '../../icons/Code';
import IconImage from '../../icons/Image';
export default function Step1({ setOptions }) {
const { nextStep } = useWizard();
const taskOptions = [
{
label: (
<>
<IconAdSense />
<span>{wizard.firstStep.taskAdSense}</span>
</>
),
value: 'google_adsense',
},
{
label: (
<>
<IconImage />
<span>{wizard.firstStep.taskImage}</span>
</>
),
value: 'ad_image',
},
{
label: (
<>
<IconCode />
<span>{wizard.firstStep.taskCode}</span>
</>
),
value: 'ad_code',
},
];
return (
<>
<p className="font-medium mt-0">{wizard.firstStep.stepHeading}</p>
<p className="mt-4 font-medium">
<label className="advads-input-radio" htmlFor="agreement">
<input
type="checkbox"
name="agreement"
id="agreement"
onChange={(event) =>
setOptions('agreement', event.target.value)
}
/>
<span
dangerouslySetInnerHTML={{
__html: wizard.firstStep.agreementText,
}}
/>
</label>
</p>
<h2>{wizard.firstStep.inputTitle}</h2>
<RadioList
id="task"
className="!mb-0"
isButton
options={taskOptions}
onChange={(value) => {
setOptions('taskOption', value);
nextStep();
}}
/>
</>
);
}