feat: conectar GA4/GSC/Drive desde la ficha (verificar + sync real)
Al guardar ID o URL en el cliente se valida con OAuth de agencia; Drive acepta URL o folder id; Orgánico se rellena si hay acceso. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -27,7 +27,7 @@ const PLACEHOLDERS: Record<string, string> = {
|
||||
clientify: 'ID / workspace Clientify',
|
||||
klaviyo: 'Account / list ID',
|
||||
mailchimp: 'Audience ID',
|
||||
google_drive: 'Folder ID (auto-match o pegar)',
|
||||
google_drive: 'URL de carpeta Drive o folder id',
|
||||
wordpress: 'https://sitio.com + Application Password',
|
||||
prestashop: 'https://tienda.com + API key',
|
||||
shopify: 'mitienda.myshopify.com + Admin API token',
|
||||
@@ -37,6 +37,8 @@ const PLACEHOLDERS: Record<string, string> = {
|
||||
linkedin: 'Company Page ID / linkedin.com/company/…',
|
||||
};
|
||||
|
||||
const LIVE_FROM_FICHA = new Set(['ga4', 'search_console', 'google_drive']);
|
||||
|
||||
export default function ClientIntegrationsPanel({
|
||||
tenantId,
|
||||
tenantName,
|
||||
@@ -52,6 +54,7 @@ export default function ClientIntegrationsPanel({
|
||||
const [integrations, setIntegrations] = useState<Integ[]>([]);
|
||||
const [providers, setProviders] = useState<Provider[]>([]);
|
||||
const [warning, setWarning] = useState<string | null>(null);
|
||||
const [flash, setFlash] = useState<string | null>(null);
|
||||
const [saving, setSaving] = useState<string | null>(null);
|
||||
const [accountDraft, setAccountDraft] = useState<Record<string, string>>({});
|
||||
const [followed, setFollowed] = useState(true);
|
||||
@@ -87,11 +90,18 @@ export default function ClientIntegrationsPanel({
|
||||
|
||||
async function patch(provider: string, body: Record<string, unknown>) {
|
||||
setSaving(provider);
|
||||
await fetch('/api/admin/integrations', {
|
||||
setFlash(null);
|
||||
const res = await fetch('/api/admin/integrations', {
|
||||
method: 'PATCH',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ tenantId, provider, ...body }),
|
||||
});
|
||||
const data = await res.json().catch(() => ({}));
|
||||
if (data.activation?.message) setFlash(data.activation.message);
|
||||
else if (data.error) setFlash(String(data.error));
|
||||
else if (LIVE_FROM_FICHA.has(provider) && body.status === 'connected') {
|
||||
setFlash(res.ok ? 'Conexión verificada' : 'No se pudo activar');
|
||||
}
|
||||
await load();
|
||||
onChanged?.();
|
||||
setSaving(null);
|
||||
@@ -168,6 +178,16 @@ export default function ClientIntegrationsPanel({
|
||||
{warning}
|
||||
</p>
|
||||
)}
|
||||
{flash && (
|
||||
<p className="text-xs text-gray-800 border border-gray-200 bg-gray-50 rounded-lg px-3 py-2">
|
||||
{flash}
|
||||
</p>
|
||||
)}
|
||||
<p className="text-[11px] text-gray-600">
|
||||
GA4, Search Console y Drive: al guardar/conectar se verifica con el
|
||||
Google de agencia (marketing@) y se sincronizan datos. URL de carpeta =
|
||||
mismo que folder id. Primero conecta Google en Agencia si aún no.
|
||||
</p>
|
||||
|
||||
{groups.map((g) => {
|
||||
const list = providers.filter((p) => p.group === g.id);
|
||||
@@ -275,8 +295,11 @@ export default function ClientIntegrationsPanel({
|
||||
disabled={saving === p.id || !enabled}
|
||||
onClick={() =>
|
||||
void patch(p.id, {
|
||||
status:
|
||||
status === 'disconnected' ? 'pending' : status,
|
||||
status: LIVE_FROM_FICHA.has(p.id)
|
||||
? 'connected'
|
||||
: status === 'disconnected'
|
||||
? 'pending'
|
||||
: status,
|
||||
externalAccountId: accountDraft[p.id] || '',
|
||||
})
|
||||
}
|
||||
@@ -284,6 +307,8 @@ export default function ClientIntegrationsPanel({
|
||||
>
|
||||
{saving === p.id ? (
|
||||
<Loader2 className="w-3 h-3 animate-spin" />
|
||||
) : LIVE_FROM_FICHA.has(p.id) ? (
|
||||
'Guardar y conectar'
|
||||
) : (
|
||||
'Guardar ID'
|
||||
)}
|
||||
@@ -291,9 +316,15 @@ export default function ClientIntegrationsPanel({
|
||||
</div>
|
||||
{p.id === 'google_drive' && (
|
||||
<p className="text-[10px] text-gray-500">
|
||||
Preferible: Agencia → Conectar Drive → Re-emparejar
|
||||
(asigna carpeta por nombre). Aquí puedes pegar un Folder
|
||||
ID manual.
|
||||
Pega la URL completa o solo el id (
|
||||
…/folders/<strong>ID</strong>). Da igual: se normaliza y
|
||||
se comprueba acceso con marketing@.
|
||||
</p>
|
||||
)}
|
||||
{(p.id === 'ga4' || p.id === 'search_console') && (
|
||||
<p className="text-[10px] text-gray-500">
|
||||
Al conectar se verifica que marketing@ vea la propiedad /
|
||||
site y se cargan datos en Orgánico.
|
||||
</p>
|
||||
)}
|
||||
{(p.id === 'wordpress' ||
|
||||
|
||||
Reference in New Issue
Block a user