feat: interactive seguimiento timeline and channel evolution

Sync Superhuman Docs seguimiento/KPI pages into the portal (never passwords)
and show clients a dynamic timeline plus per-channel kpi_weekly charts.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
4Ventures
2026-09-22 21:34:21 +02:00
parent 6607a02bff
commit 64156383ca
14 changed files with 1754 additions and 1 deletions

87
sql/seguimiento.sql Normal file
View File

@@ -0,0 +1,87 @@
-- Seguimiento: Superhuman Docs timeline + integration slot
-- Idempotent. Never stores passwords.
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',
'superhuman_docs'
));
insert into public.integrations (tenant_id, provider, status, label)
select t.id, 'superhuman_docs', 'disconnected', 'Superhuman Docs'
from public.tenants t
on conflict (tenant_id, provider) do nothing;
create table if not exists public.seguimiento_entries (
id uuid primary key default gen_random_uuid(),
tenant_id uuid not null references public.tenants(id) on delete cascade,
source text not null default 'superhuman'
check (source in ('superhuman', 'manual')),
entry_type text not null default 'topic'
check (entry_type in ('meeting', 'topic', 'kpis', 'note')),
external_id text not null,
title text not null,
summary text,
body_md text,
browser_link text,
metrics jsonb not null default '{}'::jsonb,
occurred_at timestamptz,
synced_at timestamptz not null default now(),
created_at timestamptz not null default now(),
updated_at timestamptz not null default now(),
unique (tenant_id, source, external_id)
);
create index if not exists seguimiento_entries_tenant_occurred_idx
on public.seguimiento_entries (tenant_id, occurred_at desc nulls last);
create index if not exists seguimiento_entries_tenant_synced_idx
on public.seguimiento_entries (tenant_id, synced_at desc);
alter table public.seguimiento_entries enable row level security;
drop policy if exists seguimiento_entries_tenant_select on public.seguimiento_entries;
drop policy if exists seguimiento_entries_tenant_write on public.seguimiento_entries;
create policy seguimiento_entries_tenant_select on public.seguimiento_entries
for select using (
tenant_id::text = coalesce(
auth.jwt() -> 'app_metadata' ->> 'tenant_id',
auth.jwt() -> 'user_metadata' ->> 'tenant_id'
)
);
-- Clients read-only via RLS; agency writes with service role.
create policy seguimiento_entries_tenant_write on public.seguimiento_entries
for all using (
tenant_id::text = coalesce(
auth.jwt() -> 'app_metadata' ->> 'tenant_id',
auth.jwt() -> 'user_metadata' ->> 'tenant_id'
)
)
with check (
tenant_id::text = coalesce(
auth.jwt() -> 'app_metadata' ->> 'tenant_id',
auth.jwt() -> 'user_metadata' ->> 'tenant_id'
)
);
comment on table public.seguimiento_entries is
'Client-facing seguimiento timeline (Superhuman Docs). No passwords.';