const { useEffect: useEffectSR, useRef: useRefSR, useState: useStateSR } = React;
const ALL_COMPETITORS_ID = '__all_competitors__';
const FOREPLAY_PUBLISHER_OPTIONS = [
['facebook', 'Facebook'],
['instagram', 'Instagram'],
['tiktok', 'TikTok'],
['youtube', 'YouTube'],
];
const FOREPLAY_LANGUAGE_OPTIONS = [
['en', 'English'],
['es', 'Spanish'],
['fr', 'French'],
['de', 'German'],
['pt', 'Portuguese'],
['it', 'Italian'],
['nl', 'Dutch'],
['ar', 'Arabic'],
['hi', 'Hindi'],
];
const ADSPY_CTA_OPTIONS = [
'Learn More',
'Shop Now',
'Sign Up',
'Send Message',
];
const splitFilterList = (value) => String(value || '').split(',').map(s => s.trim()).filter(Boolean);
const selectedValues = (event) => Array.from(event.target.selectedOptions || []).map(o => o.value).filter(Boolean);
const joinFilterList = (values) => [...new Set((values || []).map(v => String(v || '').trim()).filter(Boolean))].join(', ');
const ADSPY_COUNTRY_OPTIONS = ['US', 'UK', 'CA', 'AU', 'DE', 'FR', 'ES', 'IT', 'NL', 'BR'];
const ADSPY_LANGUAGE_OPTIONS = [
['eng', 'English'],
['spa', 'Spanish'],
['fre', 'French'],
['ger', 'German'],
['por', 'Portuguese'],
['ita', 'Italian'],
['dut', 'Dutch'],
];
const ADSPY_DROPDOWN_LABELS = [
'siteType', 'gender', 'ages', 'dailyLikes', 'totalLikes',
'mediaType', 'createdBetween', 'seenBetween', 'networks', 'affiliate',
'advertiser', 'technologies', 'countries', 'language', 'buttons',
];
const adspyFilterIsActive = (filters, id) => {
if (id === 'siteType') return !!filters.adspySiteType;
if (id === 'mediaType') return !!filters.adspyMediaType;
if (id === 'gender') return !!filters.adspyGender;
if (id === 'ages') return !!(filters.adspyAgeMin || filters.adspyAgeMax);
if (id === 'dailyLikes') return !!(filters.minDailyLikes || filters.maxDailyLikes);
if (id === 'totalLikes') return !!(filters.minLikes || filters.maxLikes);
if (id === 'createdBetween') return !!(filters.adspyCreatedFrom || filters.adspyCreatedTo);
if (id === 'seenBetween') return !!(filters.adspySeenFrom || filters.adspySeenTo);
if (id === 'networks') return !!filters.adspyAffNetwork;
if (id === 'affiliate') return !!(filters.adspyAffId || filters.adspyOfferId);
if (id === 'advertiser') return !!(filters.adspyUsername || filters.adspyUserId);
if (id === 'technologies') return !!filters.adspyTech;
if (id === 'countries') return !!filters.adspyCountries;
if (id === 'language') return !!filters.adspyLang;
if (id === 'buttons') return filters.adspyCtaMode === 'custom' || !!filters.adspyButtons;
return false;
};
const optionLabel = (options, value) => {
const match = (options || []).find(option => {
const optionValue = Array.isArray(option) ? option[0] : option;
return String(optionValue) === String(value);
});
if (!match) return value ? String(value) : '';
return Array.isArray(match) ? match[1] : match;
};
const compactListPreview = (value, options, maxItems = 2) => {
const values = splitFilterList(value);
if (!values.length) return '';
const labels = values.map(item => optionLabel(options, item)).filter(Boolean);
if (labels.length <= maxItems) return labels.join(', ');
return `${labels.slice(0, maxItems).join(', ')} +${labels.length - maxItems}`;
};
const rangePreview = (minValue, maxValue, { defaultMin = '', defaultMax = '', suffix = '' } = {}) => {
const minText = minValue || defaultMin;
const maxText = maxValue || defaultMax;
if (!minValue && !maxValue) return '';
if (minText && maxText) return `${minText}-${maxText}${suffix}`;
if (minText) return `Min ${minText}${suffix}`;
return `Max ${maxText}${suffix}`;
};
const numberPreview = (value) => {
const parsed = Number(value);
return Number.isFinite(parsed) ? parsed.toLocaleString() : String(value || '');
};
const AdspyDropdown = ({ id, label, preview, active, open, onToggle, onClose, onReset, children }) => {
const dropdownRef = useRefSR(null);
useEffectSR(() => {
if (!open) return undefined;
const onMouseDown = (event) => {
if (dropdownRef.current && !dropdownRef.current.contains(event.target)) {
onClose?.();
}
};
const onKeyDown = (event) => {
if (event.key === 'Escape') onClose?.();
};
document.addEventListener('mousedown', onMouseDown);
document.addEventListener('keydown', onKeyDown);
return () => {
document.removeEventListener('mousedown', onMouseDown);
document.removeEventListener('keydown', onKeyDown);
};
}, [open, onClose]);
return (
{open && (
)}
);
};
const AdspyRadio = ({ name, value, options, onChange }) => (
{options.map(([optionValue, label]) => (
))}
);
const AdspyChecks = ({ values, options, onChange }) => {
const selected = new Set(splitFilterList(values));
const toggle = (value) => {
const next = new Set(selected);
next.has(value) ? next.delete(value) : next.add(value);
onChange(joinFilterList([...next]));
};
return (
{options.map(option => {
const value = Array.isArray(option) ? option[0] : option;
const label = Array.isArray(option) ? option[1] : option;
return (
);
})}
);
};
const RANGE_UI_MAX = 1000;
const LARGE_RANGE_CURVE = 2.4;
const curvedRangeEnabled = (min, max) => min === 0 && max > 100;
const valueToSliderPosition = (value, max) => Math.round(
Math.pow(Math.max(0, Math.min(max, value)) / max, 1 / LARGE_RANGE_CURVE) * RANGE_UI_MAX,
);
const sliderPositionToValue = (position, max) => Math.round(
max * Math.pow(Math.max(0, Math.min(RANGE_UI_MAX, position)) / RANGE_UI_MAX, LARGE_RANGE_CURVE),
);
const AdspyRange = ({ label, min, max, value, onChange }) => {
const numeric = Math.max(min, Math.min(max, parseInt(value, 10) || min));
const useCurvedScale = curvedRangeEnabled(min, max);
const sliderValue = useCurvedScale ? valueToSliderPosition(numeric, max) : numeric;
const sliderMax = useCurvedScale ? RANGE_UI_MAX : max;
const displayValue = numeric.toLocaleString();
return (
);
};
const AdspyText = ({ label, value, onChange, type = 'text' }) => (
);
window.PlatformFilterSections = function PlatformFilterSections({
filters = {},
platforms = {},
onFilterChange,
onFiltersChange,
idPrefix = 'filters',
}) {
const [openForeplayFilter, setOpenForeplayFilter] = useStateSR(null);
const [openAdspyFilter, setOpenAdspyFilter] = useStateSR(null);
const [openBrandFilter, setOpenBrandFilter] = useStateSR(null);
const foreplayEnabled = !!platforms?.foreplay;
const adspyEnabled = !!platforms?.adspy;
const brandsearchEnabled = !!platforms?.brandsearch;
const setFilter = (key, value) => onFilterChange?.(key, value);
const setFilters = (patch) => {
if (onFiltersChange) onFiltersChange(patch);
else Object.entries(patch || {}).forEach(([key, value]) => setFilter(key, value));
};
const ctaOptionSet = new Set(ADSPY_CTA_OPTIONS);
const adspyCtaMode = filters.adspyCtaMode || (filters.adspyButtons ? 'custom' : 'all');
const adspyButtons = splitFilterList(filters.adspyButtons);
const selectedAdspyButtons = adspyButtons.length
? adspyButtons.filter(value => ctaOptionSet.has(value))
: ADSPY_CTA_OPTIONS;
const customAdspyButtons = adspyButtons.filter(value => !ctaOptionSet.has(value));
const setAdspyCtaMode = (mode) => {
if (mode === 'custom') {
setFilters({
adspyCtaMode: 'custom',
adspyButtons: filters.adspyButtons || joinFilterList(ADSPY_CTA_OPTIONS),
});
return;
}
setFilters({ adspyCtaMode: 'all', adspyButtons: '' });
};
const setAdspyButtons = (selected, customText) => {
setFilters({
adspyCtaMode: 'custom',
adspyButtons: joinFilterList([
...(selected || []),
...splitFilterList(customText),
]),
});
};
const resetAdspyFilter = (id) => {
const resets = {
siteType: { adspySiteType: '' },
gender: { adspyGender: '' },
ages: { adspyAgeMin: '', adspyAgeMax: '' },
dailyLikes: { minDailyLikes: '', maxDailyLikes: '' },
totalLikes: { minLikes: '', maxLikes: '' },
mediaType: { adspyMediaType: '' },
createdBetween: { adspyCreatedFrom: '', adspyCreatedTo: '' },
seenBetween: { adspySeenFrom: '', adspySeenTo: '' },
networks: { adspyAffNetwork: '' },
affiliate: { adspyAffId: '', adspyOfferId: '' },
advertiser: { adspyUsername: '', adspyUserId: '' },
technologies: { adspyTech: '' },
countries: { adspyCountries: '' },
language: { adspyLang: '' },
buttons: { adspyCtaMode: 'all', adspyButtons: '' },
};
setFilters(resets[id] || {});
};
const adspyFilterPreview = (id) => {
if (id === 'siteType') return optionLabel([['facebook', 'Facebook'], ['instagram', 'Instagram']], filters.adspySiteType);
if (id === 'mediaType') return optionLabel([['video', 'Video'], ['photo', 'Photo']], filters.adspyMediaType);
if (id === 'gender') return optionLabel([['female', 'Female'], ['male', 'Male']], filters.adspyGender);
if (id === 'ages') return rangePreview(filters.adspyAgeMin, filters.adspyAgeMax, { defaultMin: '18', defaultMax: '65' });
if (id === 'dailyLikes') return rangePreview(filters.minDailyLikes, filters.maxDailyLikes, { suffix: '/day' });
if (id === 'totalLikes') return rangePreview(filters.minLikes, filters.maxLikes, { suffix: ' likes' });
if (id === 'createdBetween') return rangePreview(filters.adspyCreatedFrom, filters.adspyCreatedTo);
if (id === 'seenBetween') return rangePreview(filters.adspySeenFrom, filters.adspySeenTo);
if (id === 'networks') return filters.adspyAffNetwork ? `ID ${filters.adspyAffNetwork}` : '';
if (id === 'affiliate') {
return [filters.adspyAffId && `Aff ${filters.adspyAffId}`, filters.adspyOfferId && `Offer ${filters.adspyOfferId}`]
.filter(Boolean)
.join(', ');
}
if (id === 'advertiser') {
return [filters.adspyUsername, filters.adspyUserId && `Page ${filters.adspyUserId}`]
.filter(Boolean)
.join(', ');
}
if (id === 'technologies') return compactListPreview(filters.adspyTech, []);
if (id === 'countries') return compactListPreview(filters.adspyCountries, ADSPY_COUNTRY_OPTIONS);
if (id === 'language') return optionLabel(ADSPY_LANGUAGE_OPTIONS, filters.adspyLang);
if (id === 'buttons') {
if (adspyCtaMode !== 'custom' && !filters.adspyButtons) return '';
return compactListPreview(filters.adspyButtons || joinFilterList(ADSPY_CTA_OPTIONS), ADSPY_CTA_OPTIONS);
}
return '';
};
const renderAdspyFilter = (id) => {
const labels = {
siteType: 'Site type',
gender: 'Gender',
ages: 'Ages',
dailyLikes: 'Daily likes',
totalLikes: 'Total likes',
mediaType: 'Media type',
createdBetween: 'Created Between',
seenBetween: 'Seen Between',
networks: 'Networks',
affiliate: 'Affiliate',
advertiser: 'Advertiser',
technologies: 'Technologies',
countries: 'Countries',
language: 'Language',
buttons: 'Buttons',
};
let body = null;
if (id === 'siteType') {
body = (
setFilter('adspySiteType', value)}
options={[['', 'Facebook or Instagram'], ['facebook', 'Facebook'], ['instagram', 'Instagram']]}
/>
);
} else if (id === 'gender') {
body = (
setFilter('adspyGender', value)}
options={[['', 'Any gender'], ['female', 'Female'], ['male', 'Male']]}
/>
);
} else if (id === 'ages') {
const minAge = filters.adspyAgeMin || '18';
const maxAge = filters.adspyAgeMax || '65';
body = (
<>
From {minAge} to {maxAge}{Number(maxAge) >= 65 ? ' +' : ''}
setFilter('adspyAgeMin', value)} />
setFilter('adspyAgeMax', value)} />
>
);
} else if (id === 'dailyLikes') {
body = (
<>
setFilter('minDailyLikes', value)} />
setFilter('maxDailyLikes', value)} />
>
);
} else if (id === 'totalLikes') {
body = (
<>
setFilter('minLikes', value)} />
setFilter('maxLikes', value)} />
>
);
} else if (id === 'mediaType') {
body = (
setFilter('adspyMediaType', value)}
options={[['', 'Any media type'], ['video', 'Video'], ['photo', 'Photo']]}
/>
);
} else if (id === 'createdBetween') {
body = (
<>
setFilter('adspyCreatedFrom', value)} />
setFilter('adspyCreatedTo', value)} />
>
);
} else if (id === 'seenBetween') {
body = (
<>
setFilter('adspySeenFrom', value)} />
setFilter('adspySeenTo', value)} />
>
);
} else if (id === 'networks') {
body = (
setFilter('adspyAffNetwork', value)}
/>
);
} else if (id === 'affiliate') {
body = (
<>
setFilter('adspyAffId', value)} />
setFilter('adspyOfferId', value)} />
>
);
} else if (id === 'advertiser') {
body = (
<>
setFilter('adspyUsername', value)} />
setFilter('adspyUserId', value)} />
>
);
} else if (id === 'technologies') {
body = (
setFilter('adspyTech', value)}
/>
);
} else if (id === 'countries') {
body = setFilter('adspyCountries', value)} />;
} else if (id === 'language') {
body = setFilter('adspyLang', value)} options={[['', 'Any language'], ...ADSPY_LANGUAGE_OPTIONS]} />;
} else if (id === 'buttons') {
body = (
<>
{adspyCtaMode === 'custom' && (
<>
setAdspyButtons(splitFilterList(value), customAdspyButtons.join(', '))} />
setAdspyButtons(selectedAdspyButtons, value)} />
>
)}
>
);
}
return (
setOpenAdspyFilter(openAdspyFilter === id ? null : id)}
onClose={() => setOpenAdspyFilter(null)}
onReset={() => resetAdspyFilter(id)}
>
{body}
);
};
const brandSlider = ({ id, label, filterKey, max, suffix = '' }) => (
setOpenBrandFilter(openBrandFilter === id ? null : id)}
onClose={() => setOpenBrandFilter(null)}
onReset={() => setFilter(filterKey, '')}
>
setFilter(filterKey, value)}
/>
{suffix && Value is sent as minimum {suffix}.
}
);
return (
<>
{foreplayEnabled && (
)}
{adspyEnabled && (
AdSpy filters
{ADSPY_DROPDOWN_LABELS.map(renderAdspyFilter)}
)}
{brandsearchEnabled && (
BrandSearch filters
{brandSlider({ id: 'bs-views', label: 'Views', filterKey: 'bsMinViews', max: 1000000, suffix: 'views/plays' })}
{brandSlider({ id: 'bs-likes', label: 'Likes', filterKey: 'bsMinLikes', max: 100000, suffix: 'likes' })}
{brandSlider({ id: 'bs-er', label: 'ER%', filterKey: 'bsMinEngagement', max: 100, suffix: 'engagement rate' })}
{brandSlider({ id: 'bs-reach', label: 'Reach', filterKey: 'bsMinReach', max: 1000000, suffix: 'reach' })}
{brandSlider({ id: 'bs-min-spend', label: 'Min spend', filterKey: 'bsMinSpend', max: 100000, suffix: 'spend' })}
{brandSlider({ id: 'bs-max-spend', label: 'Max spend', filterKey: 'bsMaxSpend', max: 100000, suffix: 'spend cap' })}
)}
>
);
};
window.SearchRow = function SearchRow({ row, onChange, onRemove, onSearch, searching, canRemove, competitors }) {
const filters = row.filters || {};
const togglePlatform = (p) => {
const next = {
foreplay: !!row.platforms?.foreplay,
adspy: !!row.platforms?.adspy,
brandsearch: !!row.platforms?.brandsearch,
metaads: false,
};
next[p] = !next[p];
onChange({ ...row, platforms: next });
};
const selectAllPlatforms = () => onChange({
...row,
platforms: { foreplay: true, adspy: true, brandsearch: true, metaads: false },
});
const setFilter = (key, value) => {
onChange({ ...row, filters: { ...filters, [key]: value } });
};
const setFilters = (patch) => {
onChange({ ...row, filters: { ...filters, ...patch } });
};
const onCompetitor = (e) => {
const competitorId = e.target.value || null;
onChange({ ...row, competitorId });
};
const allCompetitorsSet = row.competitorId === ALL_COMPETITORS_ID;
const competitorSet = !!row.competitorId;
const sortedCompetitors = [...(competitors || [])].sort((a, b) => {
const labelA = String(a?.name || a?.id || '').toLowerCase();
const labelB = String(b?.name || b?.id || '').toLowerCase();
return labelA.localeCompare(labelB) || String(a?.id || '').localeCompare(String(b?.id || ''));
});
const foreplayEnabled = !!row.platforms?.foreplay;
const adspyEnabled = !!row.platforms?.adspy;
const brandsearchEnabled = !!row.platforms?.brandsearch;
return (
);
};