feat: add Instagram, Facebook, TikTok and LinkedIn slots

Organic social channels per client in the agency hub, separate from
paid Meta Ads.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
4Ventures
2026-09-22 18:58:29 +02:00
parent 670679d09c
commit eb00256bea
5 changed files with 66 additions and 8 deletions

View File

@@ -0,0 +1,36 @@
-- Organic social connection slots per tenant
-- Idempotent. (Distinct from meta_ads / paid.)
alter table public.integrations drop constraint if exists integrations_provider_check;
alter table public.integrations
add constraint integrations_provider_check
check (provider in (
'meta_ads',
'google_ads',
'ga4',
'search_console',
'tag_manager',
'clientify',
'klaviyo',
'mailchimp',
'google_drive',
'wordpress',
'prestashop',
'shopify',
'instagram',
'facebook',
'tiktok',
'linkedin'
));
insert into public.integrations (tenant_id, provider, status, label)
select t.id, p.provider, 'disconnected', p.label
from public.tenants t
cross join (
values
('instagram', 'Instagram'),
('facebook', 'Facebook Page'),
('tiktok', 'TikTok'),
('linkedin', 'LinkedIn')
) as p(provider, label)
on conflict (tenant_id, provider) do nothing;

View File

@@ -58,6 +58,10 @@ export async function POST(req: NextRequest) {
['wordpress', 'WordPress'], ['wordpress', 'WordPress'],
['prestashop', 'PrestaShop'], ['prestashop', 'PrestaShop'],
['shopify', 'Shopify'], ['shopify', 'Shopify'],
['instagram', 'Instagram'],
['facebook', 'Facebook Page'],
['tiktok', 'TikTok'],
['linkedin', 'LinkedIn'],
]; ];
await fetch(`${url}/rest/v1/integrations`, { await fetch(`${url}/rest/v1/integrations`, {
method: 'POST', method: 'POST',

View File

@@ -54,18 +54,18 @@ type AlertRow = {
const MATRIX_PROVIDERS = [ const MATRIX_PROVIDERS = [
'meta_ads', 'meta_ads',
'google_ads', 'instagram',
'clientify', 'tiktok',
'linkedin',
'google_drive', 'google_drive',
'wordpress',
] as const; ] as const;
const SHORT: Record<string, string> = { const SHORT: Record<string, string> = {
meta_ads: 'Meta', meta_ads: 'Meta',
google_ads: 'GAds', instagram: 'IG',
clientify: 'CRM', tiktok: 'TT',
linkedin: 'LI',
google_drive: 'Drive', google_drive: 'Drive',
wordpress: 'WP',
}; };
function healthBadge(h: string) { function healthBadge(h: string) {
@@ -241,8 +241,8 @@ export default function AgencyPortfolioBoard() {
Panel de control · clientes y conexiones Panel de control · clientes y conexiones
</h3> </h3>
<p className="text-[11px] text-gray-500 mt-0.5"> <p className="text-[11px] text-gray-500 mt-0.5">
Cada cliente con su Meta, Google Ads, Clientify, Drive y web Cada cliente con ads, redes (IG/TikTok/LinkedIn), Drive y web.
(WordPress…). Activa/apaga canales en Conexiones. Activa/apaga canales en Conexiones.
</p> </p>
</div> </div>
<button <button

View File

@@ -31,6 +31,10 @@ const PLACEHOLDERS: Record<string, string> = {
wordpress: 'https://sitio.com + Application Password', wordpress: 'https://sitio.com + Application Password',
prestashop: 'https://tienda.com + API key', prestashop: 'https://tienda.com + API key',
shopify: 'mitienda.myshopify.com + Admin API token', shopify: 'mitienda.myshopify.com + Admin API token',
instagram: '@handle o Instagram Business Account ID',
facebook: 'Page ID / facebook.com/…',
tiktok: '@handle o Advertiser / Business ID',
linkedin: 'Company Page ID / linkedin.com/company/…',
}; };
export default function ClientIntegrationsPanel({ export default function ClientIntegrationsPanel({
@@ -106,6 +110,7 @@ export default function ClientIntegrationsPanel({
const groups = [ const groups = [
{ id: 'paid', title: 'Paid media' }, { id: 'paid', title: 'Paid media' },
{ id: 'social', title: 'Redes sociales (orgánico)' },
{ id: 'analytics', title: 'Analítica y medición' }, { id: 'analytics', title: 'Analítica y medición' },
{ id: 'crm', title: 'CRM' }, { id: 'crm', title: 'CRM' },
{ id: 'email', title: 'Email / lifecycle' }, { id: 'email', title: 'Email / lifecycle' },
@@ -291,6 +296,15 @@ export default function ClientIntegrationsPanel({
fase. fase.
</p> </p>
)} )}
{(p.id === 'instagram' ||
p.id === 'facebook' ||
p.id === 'tiktok' ||
p.id === 'linkedin') && (
<p className="text-[10px] text-gray-500">
Orgánico (páginas/perfiles). Distinto de Meta Ads /
TikTok Ads. OAuth live y publicación: siguiente fase.
</p>
)}
</li> </li>
); );
})} })}

View File

@@ -13,6 +13,10 @@ export const INTEGRATION_PROVIDERS = [
{ id: 'wordpress', label: 'WordPress', group: 'cms' }, { id: 'wordpress', label: 'WordPress', group: 'cms' },
{ id: 'prestashop', label: 'PrestaShop', group: 'cms' }, { id: 'prestashop', label: 'PrestaShop', group: 'cms' },
{ id: 'shopify', label: 'Shopify', group: 'cms' }, { id: 'shopify', label: 'Shopify', group: 'cms' },
{ id: 'instagram', label: 'Instagram', group: 'social' },
{ id: 'facebook', label: 'Facebook Page', group: 'social' },
{ id: 'tiktok', label: 'TikTok', group: 'social' },
{ id: 'linkedin', label: 'LinkedIn', group: 'social' },
] as const; ] as const;
export type IntegrationProviderId = export type IntegrationProviderId =