feat: al conectar Google, auto-emparejar Drive/GA4/GSC/Ads en fichas

Bootstrap unificado post-OAuth + botón Emparejar todo. Meta discover
también intenta FB/IG pages con el token system user (API 4V2030).

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
4Ventures
2026-09-24 12:41:51 +02:00
parent 99895fe918
commit 7ed6e641cb
8 changed files with 374 additions and 75 deletions

View File

@@ -54,40 +54,36 @@ export default function GoogleOrganicPanel() {
useEffect(() => {
const params = new URLSearchParams(window.location.search);
if (params.get('drive') === 'ok') {
const boot = params.get('boot');
setMsg(
'Google reconectado. Pulsa «Descubrir y emparejar» para GA4/GSC.'
boot
? `Google reconectado · ${boot}`
: 'Google reconectado. Emparejando fichas…'
);
}
void loadStatus();
}, []);
async function discover(apply: boolean) {
async function bootstrapAll() {
setBusy('match');
setErr(null);
const res = await fetch('/api/admin/google/organic', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ action: 'match', apply }),
body: JSON.stringify({ action: 'bootstrap', days: 28 }),
});
const data = await res.json();
if (!res.ok) {
setBusy(null);
setErr(data.error || 'Discover falló');
setBusy(null);
if (!res.ok && !data.message) {
setErr(data.error || 'Emparejado falló');
return;
}
setMatches(data.matches || []);
setGa4Count(data.ga4Count || 0);
setGscCount(data.gscCount || 0);
setMsg(
apply
? `Emparejado · GA4 ${data.applied?.ga4 || 0} · GSC ${data.applied?.gsc || 0}. Actualizando datos…`
: `Vistas ${data.ga4Count} props GA4 · ${data.gscCount} sites GSC (sin escribir)`
);
if (apply) {
await syncAll();
} else {
setBusy(null);
setMsg(data.message || 'Emparejado listo');
if (data.organic) {
setGa4Count(data.organic.ga4Visible || 0);
setGscCount(data.organic.gscVisible || 0);
}
await loadStatus();
}
async function syncAll() {
@@ -109,7 +105,7 @@ export default function GoogleOrganicPanel() {
0
);
setMsg(
`Datos al día · ${data.synced || 0} clientes · ${wrote} días en Orgánico (se actualiza solo al abrir la vista y cada noche)`
`Datos al día · ${data.synced || 0} clientes · ${wrote} días en Orgánico`
);
}
@@ -128,12 +124,12 @@ export default function GoogleOrganicPanel() {
<Leaf className="w-5 h-5 text-lime-600 mt-0.5 shrink-0" />
<div>
<h3 className="text-sm font-semibold text-gray-900">
2 · Orgánico (GA4 + Search Console)
2 · Orgánico + emparejado Google
</h3>
<p className="text-xs text-gray-600 mt-0.5 max-w-xl">
No es otra cuenta: usa la conexión del panel 1. Aquí solo
emparejas propiedades/sites a cada cliente y actualizas datos.
Luego se mantienen al día solos.
Al conectar Google (panel 1) se rellenan solos Drive, GA4, GSC y
Google Ads en cada ficha. Aquí puedes repetir el emparejado y
actualizar Orgánico.
</p>
</div>
</div>
@@ -141,7 +137,7 @@ export default function GoogleOrganicPanel() {
<button
type="button"
disabled={!connected || busy !== null}
onClick={() => void discover(true)}
onClick={() => void bootstrapAll()}
className="inline-flex items-center gap-1.5 text-xs font-semibold px-3 py-1.5 rounded-lg border border-gray-200 text-gray-800 hover:bg-gray-50 disabled:opacity-40"
>
{busy === 'match' ? (
@@ -149,7 +145,7 @@ export default function GoogleOrganicPanel() {
) : (
<Search className="w-3.5 h-3.5" />
)}
Descubrir y emparejar
Emparejar todo Google
</button>
<button
type="button"