@fayz-ai/plugin-marketing 0.8.1 → 0.8.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/dist/MarketingPage.d.ts.map +1 -1
  2. package/dist/data/capability.test.d.ts +2 -0
  3. package/dist/data/capability.test.d.ts.map +1 -0
  4. package/dist/data/supabase.d.ts +10 -0
  5. package/dist/data/supabase.d.ts.map +1 -0
  6. package/dist/data/tables.d.ts +7 -0
  7. package/dist/data/tables.d.ts.map +1 -1
  8. package/dist/data/types.d.ts +2 -0
  9. package/dist/data/types.d.ts.map +1 -1
  10. package/dist/index.cjs +744 -58
  11. package/dist/index.cjs.map +1 -1
  12. package/dist/index.d.ts +1 -0
  13. package/dist/index.d.ts.map +1 -1
  14. package/dist/index.js +746 -61
  15. package/dist/index.js.map +1 -1
  16. package/dist/migrations/index.d.ts +3 -0
  17. package/dist/migrations/index.d.ts.map +1 -1
  18. package/dist/registries.d.ts +3 -3
  19. package/dist/registries.d.ts.map +1 -1
  20. package/dist/schema/index.d.ts +354 -0
  21. package/dist/schema/index.d.ts.map +1 -1
  22. package/dist/schema.cjs +30 -0
  23. package/dist/schema.cjs.map +1 -1
  24. package/dist/schema.js +30 -2
  25. package/dist/schema.js.map +1 -1
  26. package/dist/store.d.ts +2 -0
  27. package/dist/store.d.ts.map +1 -1
  28. package/dist/views/CampaignsView.d.ts.map +1 -1
  29. package/dist/views/blog/BlogEntityForm.d.ts.map +1 -1
  30. package/dist/views/content/ContentView.d.ts.map +1 -1
  31. package/package.json +8 -6
  32. package/src/MarketingPage.tsx +5 -1
  33. package/src/data/capability.test.ts +92 -0
  34. package/src/data/supabase.ts +401 -0
  35. package/src/data/tables.ts +9 -0
  36. package/src/data/types.ts +2 -0
  37. package/src/index.ts +138 -10
  38. package/src/migrations/005_analytics_base.sql +60 -0
  39. package/src/migrations/006_analytics_views.sql +68 -0
  40. package/src/migrations/007_agent_rpcs.sql +119 -0
  41. package/src/migrations/index.ts +257 -1
  42. package/src/registries.ts +18 -6
  43. package/src/schema/index.ts +38 -1
  44. package/src/store.ts +21 -8
  45. package/src/views/CampaignsView.tsx +4 -1
  46. package/src/views/blog/BlogEntityForm.tsx +11 -2
  47. package/src/views/content/ContentView.tsx +8 -1
@@ -3,6 +3,9 @@ export declare const MIGRATION_001_CONTENT_PLANNER = "-- Marketing Plugin: Conte
3
3
  export declare const MIGRATION_002_MULTI_PLATFORM = "-- Marketing Plugin: multi-platform accounts + per-post targets.\n-- An account is a brand/profile that publishes to MANY platforms (Instagram,\n-- YouTube, TikTok, ...). Posts optionally narrow the target platforms; an\n-- empty array means \"inherit the account's platforms\".\n\nALTER TABLE public.plg_marketing_social_accounts\n ADD COLUMN IF NOT EXISTS platforms text[] NOT NULL DEFAULT '{instagram}';\n\n-- Backfill from the legacy single-platform column, then drop it (also drops\n-- its CHECK constraint \u2014 the platform list is UI-driven from here on).\nDO $$\nBEGIN\n IF EXISTS (\n SELECT 1 FROM information_schema.columns\n WHERE table_schema = 'public' AND table_name = 'plg_marketing_social_accounts' AND column_name = 'platform'\n ) THEN\n UPDATE public.plg_marketing_social_accounts\n SET platforms = ARRAY[platform]\n WHERE platform IS NOT NULL AND platforms = '{instagram}';\n ALTER TABLE public.plg_marketing_social_accounts DROP COLUMN platform;\n END IF;\nEND $$;\n\nALTER TABLE public.plg_marketing_content_posts\n ADD COLUMN IF NOT EXISTS platforms text[] NOT NULL DEFAULT '{}';\n";
4
4
  export declare const MIGRATION_003_RECORDING_OPS = "-- Marketing Plugin: recording-day ops on posts.\n-- checklist: shooting checklist items [{ id, text, done }] \u2014 the on-set\n-- companion for recording day. media_url: uploaded asset (static posts get a\n-- caption + final art instead of a script).\n\nALTER TABLE public.plg_marketing_content_posts\n ADD COLUMN IF NOT EXISTS checklist jsonb NOT NULL DEFAULT '[]';\n\nALTER TABLE public.plg_marketing_content_posts\n ADD COLUMN IF NOT EXISTS media_url text;\n\n-- Public media bucket for content assets (mirrors the avatars-bucket pattern:\n-- public read, authenticated write). Path convention: content/{tenantId}/{postId}-{ts}.{ext}\nINSERT INTO storage.buckets (id, name, public)\n VALUES ('mkt-media', 'mkt-media', true)\n ON CONFLICT (id) DO NOTHING;\n\nDROP POLICY IF EXISTS plg_marketing_media_read ON storage.objects;\nDROP POLICY IF EXISTS plg_marketing_media_insert ON storage.objects;\nDROP POLICY IF EXISTS plg_marketing_media_update ON storage.objects;\nDROP POLICY IF EXISTS plg_marketing_media_delete ON storage.objects;\nCREATE POLICY plg_marketing_media_read ON storage.objects FOR SELECT TO public USING (bucket_id = 'mkt-media');\nCREATE POLICY plg_marketing_media_insert ON storage.objects FOR INSERT TO authenticated WITH CHECK (bucket_id = 'mkt-media');\nCREATE POLICY plg_marketing_media_update ON storage.objects FOR UPDATE TO authenticated USING (bucket_id = 'mkt-media');\nCREATE POLICY plg_marketing_media_delete ON storage.objects FOR DELETE TO authenticated USING (bucket_id = 'mkt-media');\n";
5
5
  export declare const MIGRATION_004_RANKLAYER = "-- Marketing Plugin: RankLayer integration (SEO content publishing).\n-- Control-plane state for the RankLayer connector rendered in Marketing \u2192\n-- Integra\u00E7\u00F5es. Prefix: plg_marketing_ranklayer_. Scaffold: the real sync\n-- (edge function + RankLayer API calls) lands via an external PR \u2014 see\n-- src/integrations/ranklayer/RANKLAYER.md.\n\nCREATE TABLE IF NOT EXISTS public.plg_marketing_ranklayer_integrations (\n id uuid PRIMARY KEY DEFAULT gen_random_uuid(),\n tenant_id uuid NOT NULL REFERENCES public.tenants(id) ON DELETE CASCADE,\n api_key text,\n site_domain text,\n active boolean NOT NULL DEFAULT true,\n last_sync_at timestamptz,\n created_at timestamptz NOT NULL DEFAULT now(),\n updated_at timestamptz NOT NULL DEFAULT now()\n);\nALTER TABLE public.plg_marketing_ranklayer_integrations ENABLE ROW LEVEL SECURITY;\nCREATE UNIQUE INDEX IF NOT EXISTS uq_plg_marketing_ranklayer_integrations_tenant\n ON public.plg_marketing_ranklayer_integrations(tenant_id);\n\nCREATE TABLE IF NOT EXISTS public.plg_marketing_ranklayer_sync_log (\n id uuid PRIMARY KEY DEFAULT gen_random_uuid(),\n tenant_id uuid NOT NULL REFERENCES public.tenants(id) ON DELETE CASCADE,\n integration_id uuid REFERENCES public.plg_marketing_ranklayer_integrations(id) ON DELETE CASCADE,\n status text NOT NULL DEFAULT 'success' CHECK (status IN ('success', 'partial', 'error')),\n message text,\n fetched integer NOT NULL DEFAULT 0,\n written integer NOT NULL DEFAULT 0,\n created_at timestamptz NOT NULL DEFAULT now()\n);\nALTER TABLE public.plg_marketing_ranklayer_sync_log ENABLE ROW LEVEL SECURITY;\nCREATE INDEX IF NOT EXISTS idx_plg_marketing_ranklayer_sync_log_tenant\n ON public.plg_marketing_ranklayer_sync_log(tenant_id, created_at DESC);\n\n-- RLS: authenticated tenant CRUD\nDROP POLICY IF EXISTS plg_marketing_ranklayer_integrations_select ON public.plg_marketing_ranklayer_integrations;\nDROP POLICY IF EXISTS plg_marketing_ranklayer_integrations_insert ON public.plg_marketing_ranklayer_integrations;\nDROP POLICY IF EXISTS plg_marketing_ranklayer_integrations_update ON public.plg_marketing_ranklayer_integrations;\nDROP POLICY IF EXISTS plg_marketing_ranklayer_integrations_delete ON public.plg_marketing_ranklayer_integrations;\nCREATE POLICY plg_marketing_ranklayer_integrations_select ON public.plg_marketing_ranklayer_integrations FOR SELECT TO authenticated USING (tenant_id IN (SELECT public.user_tenant_ids()));\nCREATE POLICY plg_marketing_ranklayer_integrations_insert ON public.plg_marketing_ranklayer_integrations FOR INSERT TO authenticated WITH CHECK (tenant_id IN (SELECT public.user_tenant_ids()));\nCREATE POLICY plg_marketing_ranklayer_integrations_update ON public.plg_marketing_ranklayer_integrations FOR UPDATE TO authenticated USING (tenant_id IN (SELECT public.user_tenant_ids()));\nCREATE POLICY plg_marketing_ranklayer_integrations_delete ON public.plg_marketing_ranklayer_integrations FOR DELETE TO authenticated USING (tenant_id IN (SELECT public.user_tenant_ids()));\nGRANT SELECT, INSERT, UPDATE, DELETE ON public.plg_marketing_ranklayer_integrations TO authenticated;\n\nDROP POLICY IF EXISTS plg_marketing_ranklayer_sync_log_select ON public.plg_marketing_ranklayer_sync_log;\nDROP POLICY IF EXISTS plg_marketing_ranklayer_sync_log_insert ON public.plg_marketing_ranklayer_sync_log;\nCREATE POLICY plg_marketing_ranklayer_sync_log_select ON public.plg_marketing_ranklayer_sync_log FOR SELECT TO authenticated USING (tenant_id IN (SELECT public.user_tenant_ids()));\nCREATE POLICY plg_marketing_ranklayer_sync_log_insert ON public.plg_marketing_ranklayer_sync_log FOR INSERT TO authenticated WITH CHECK (tenant_id IN (SELECT public.user_tenant_ids()));\nGRANT SELECT, INSERT ON public.plg_marketing_ranklayer_sync_log TO authenticated;\n";
6
+ export declare const MIGRATION_005_ANALYTICS_BASE = "-- Marketing Plugin: analytics base tables (channels + campaigns).\n-- Prefix: plg_marketing_\n-- Channels are tenant-owned rows lazily seeded from the app's domain preset\n-- (channel_key = preset/config id). Campaign performance is DERIVED at read\n-- time from v_marketing_attribution (006); only spend is stored.\n-- Canonical RLS form: tenant_id IN (SELECT public.user_tenant_ids()).\n\nCREATE TABLE IF NOT EXISTS public.plg_marketing_channels (\n id uuid PRIMARY KEY DEFAULT gen_random_uuid(),\n tenant_id uuid NOT NULL REFERENCES public.tenants(id) ON DELETE CASCADE,\n channel_key text NOT NULL,\n label text NOT NULL,\n icon text,\n kind text NOT NULL DEFAULT 'organic'\n CHECK (kind IN ('paid', 'organic', 'social', 'referral', 'direct', 'outbound')),\n is_active boolean NOT NULL DEFAULT true,\n monthly_spend numeric NOT NULL DEFAULT 0,\n created_at timestamptz NOT NULL DEFAULT now(),\n updated_at timestamptz NOT NULL DEFAULT now()\n);\nCREATE INDEX IF NOT EXISTS idx_plg_marketing_channels_tenant ON public.plg_marketing_channels(tenant_id);\nCREATE UNIQUE INDEX IF NOT EXISTS u_plg_marketing_channels_tenant_key ON public.plg_marketing_channels(tenant_id, channel_key);\n\nCREATE TABLE IF NOT EXISTS public.plg_marketing_campaigns (\n id uuid PRIMARY KEY DEFAULT gen_random_uuid(),\n tenant_id uuid NOT NULL REFERENCES public.tenants(id) ON DELETE CASCADE,\n name text NOT NULL,\n channel_key text NOT NULL,\n status text NOT NULL DEFAULT 'draft'\n CHECK (status IN ('active', 'paused', 'ended', 'draft')),\n starts_at timestamptz,\n ends_at timestamptz,\n spend numeric NOT NULL DEFAULT 0,\n created_at timestamptz NOT NULL DEFAULT now(),\n updated_at timestamptz NOT NULL DEFAULT now()\n);\nCREATE INDEX IF NOT EXISTS idx_plg_marketing_campaigns_tenant ON public.plg_marketing_campaigns(tenant_id);\nCREATE INDEX IF NOT EXISTS idx_plg_marketing_campaigns_channel ON public.plg_marketing_campaigns(tenant_id, channel_key);\n\nDO $$\nDECLARE t text;\nBEGIN\n FOR t IN SELECT unnest(ARRAY['plg_marketing_channels','plg_marketing_campaigns'])\n LOOP\n EXECUTE format('ALTER TABLE public.%I ENABLE ROW LEVEL SECURITY', t);\n EXECUTE format('GRANT SELECT, INSERT, UPDATE, DELETE ON public.%I TO authenticated', t);\n IF NOT EXISTS (SELECT 1 FROM pg_policies WHERE schemaname='public' AND tablename=t AND policyname=t||'_select') THEN\n EXECUTE format('CREATE POLICY %I ON public.%I FOR SELECT TO authenticated USING (tenant_id IN (SELECT public.user_tenant_ids()))', t||'_select', t);\n END IF;\n IF NOT EXISTS (SELECT 1 FROM pg_policies WHERE schemaname='public' AND tablename=t AND policyname=t||'_insert') THEN\n EXECUTE format('CREATE POLICY %I ON public.%I FOR INSERT TO authenticated WITH CHECK (tenant_id IN (SELECT public.user_tenant_ids()))', t||'_insert', t);\n END IF;\n IF NOT EXISTS (SELECT 1 FROM pg_policies WHERE schemaname='public' AND tablename=t AND policyname=t||'_update') THEN\n EXECUTE format('CREATE POLICY %I ON public.%I FOR UPDATE TO authenticated USING (tenant_id IN (SELECT public.user_tenant_ids()))', t||'_update', t);\n END IF;\n IF NOT EXISTS (SELECT 1 FROM pg_policies WHERE schemaname='public' AND tablename=t AND policyname=t||'_delete') THEN\n EXECUTE format('CREATE POLICY %I ON public.%I FOR DELETE TO authenticated USING (tenant_id IN (SELECT public.user_tenant_ids()))', t||'_delete', t);\n END IF;\n END LOOP;\nEND $$;\n";
7
+ export declare const MIGRATION_006_ANALYTICS_VIEWS = "-- Marketing Plugin: analytics read views.\n-- v_marketing_channels / v_marketing_campaigns \u2014 bridge views for the surface\n-- and the agent's data primitives.\n-- v_marketing_attribution \u2014 GENERIC, spine-only attribution event stream\n-- (people/appointments/orders metadata). `channel_raw` is the unnormalized\n-- origin string; the provider matches it against the app's channel set\n-- (id or label, case/punctuation-insensitive).\n-- Apps whose origin lives in an app-local extension table (e.g. beauty's\n-- clients.origin) may CREATE OR REPLACE this view in their own incubator\n-- migration, KEEPING the column contract:\n-- (tenant_id uuid, kind text, source text, channel_raw text,\n-- occurred_at timestamptz, value numeric)\n\nCREATE OR REPLACE VIEW public.v_marketing_channels WITH (security_invoker=true) AS\nSELECT c.id, c.tenant_id, c.channel_key, c.label, c.icon, c.kind,\n c.is_active, c.monthly_spend, c.created_at, c.updated_at\nFROM public.plg_marketing_channels c;\nGRANT SELECT ON public.v_marketing_channels TO authenticated;\n\nCREATE OR REPLACE VIEW public.v_marketing_campaigns WITH (security_invoker=true) AS\nSELECT g.id, g.tenant_id, g.name, g.channel_key, ch.label AS channel_label,\n g.status, g.starts_at, g.ends_at, g.spend, g.created_at, g.updated_at\nFROM public.plg_marketing_campaigns g\nLEFT JOIN public.plg_marketing_channels ch\n ON ch.tenant_id = g.tenant_id AND ch.channel_key = g.channel_key;\nGRANT SELECT ON public.v_marketing_campaigns TO authenticated;\n\nCREATE OR REPLACE VIEW public.v_marketing_attribution WITH (security_invoker=true) AS\n-- lead events (CRM leads; sourceId aligns with lead-sources/channel ids)\nSELECT p.tenant_id,\n 'lead'::text AS kind,\n 'crm'::text AS source,\n COALESCE(p.metadata->>'sourceId', p.metadata->>'sourceName', p.metadata->>'origin') AS channel_raw,\n p.created_at AS occurred_at,\n NULLIF(p.metadata->>'value', '')::numeric AS value\nFROM public.people p\nWHERE p.kind = 'lead'\nUNION ALL\n-- booking conversions (agenda verticals)\nSELECT b.tenant_id, 'conversion', 'agenda',\n COALESCE(b.metadata->>'origin', pc.metadata->>'origin', pc.metadata->>'sourceId', pc.metadata->>'sourceName'),\n b.starts_at,\n o.total\nFROM public.appointments b\nLEFT JOIN public.people pc ON pc.id = b.party_id\nLEFT JOIN public.orders o ON o.id = b.order_id\nWHERE b.status NOT IN ('cancelled', 'no_show')\nUNION ALL\n-- order conversions (commerce verticals)\nSELECT o.tenant_id, 'conversion', 'orders',\n COALESCE(o.metadata->>'origin', pp.metadata->>'origin', pp.metadata->>'sourceId'),\n o.created_at,\n o.total\nFROM public.orders o\nLEFT JOIN public.people pp ON pp.id = o.party_id\nWHERE o.kind NOT IN ('appointment', 'deal')\n AND o.status NOT IN ('cancelled', 'draft')\nUNION ALL\n-- won-deal conversions (CRM verticals)\nSELECT o.tenant_id, 'conversion', 'crm',\n COALESCE(pp.metadata->>'sourceId', pp.metadata->>'sourceName', o.metadata->>'origin'),\n o.updated_at,\n o.total\nFROM public.orders o\nLEFT JOIN public.people pp ON pp.id = o.party_id\nWHERE o.kind = 'deal' AND o.status = 'won';\n\nGRANT SELECT ON public.v_marketing_attribution TO authenticated;\n";
8
+ export declare const MIGRATION_007_AGENT_RPCS = "-- ============================================================================\n-- plugin-marketing 007: server-plane agent write RPC.\n--\n-- public.agent_marketing_create_campaign \u2014 guarded campaign create for the\n-- assistant. Mirrors agent_agenda_create_appointment (agenda 005):\n-- * actor-authorized: public.agent_guard (spine 015) runs role\u2192plan\u2192limit\n-- BEFORE anything \u2014 denial comes back as structured jsonb;\n-- * channel is validated against the tenant's plg_marketing_channels rows\n-- (key or label, case/punctuation-insensitive); when the tenant has no\n-- channel rows yet (surface not opened \u2192 lazy seed not run) the raw key is\n-- accepted as-is;\n-- * audited: audit_logs row with the acting user.\n--\n-- Contract (all agent_* RPCs): (p_tenant_id, p_actor_user_id, p_payload jsonb)\n-- \u2192 jsonb {ok:true, id, record:{...}} | {ok:false, denial:{...}} | {ok:false,\n-- error text}. GRANT authenticated+service_role \u2014 NEVER anon.\n-- ============================================================================\n\nCREATE OR REPLACE FUNCTION public.agent_marketing_create_campaign(\n p_tenant_id uuid,\n p_actor_user_id uuid,\n p_payload jsonb\n) RETURNS jsonb\nLANGUAGE plpgsql SECURITY DEFINER\nSET search_path = public\nAS $$\nDECLARE\n v_denial jsonb;\n v_used int;\n v_name text;\n v_channel_raw text;\n v_channel_key text;\n v_status text;\n v_starts timestamptz;\n v_ends timestamptz;\n v_spend numeric;\n v_has_channels boolean;\n v_id uuid;\nBEGIN\n -- \u2500\u2500 payload \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n v_name := left(trim(p_payload->>'name'), 200);\n v_channel_raw := trim(p_payload->>'channel_key');\n v_status := COALESCE(NULLIF(trim(p_payload->>'status'), ''), 'draft');\n v_spend := COALESCE(NULLIF(p_payload->>'spend', '')::numeric, 0);\n BEGIN\n v_starts := NULLIF(p_payload->>'starts_at', '')::timestamptz;\n v_ends := NULLIF(p_payload->>'ends_at', '')::timestamptz;\n EXCEPTION WHEN OTHERS THEN\n RETURN jsonb_build_object('ok', false, 'error', 'invalid payload: ' || SQLERRM);\n END;\n IF v_name IS NULL OR v_name = '' OR v_channel_raw IS NULL OR v_channel_raw = '' THEN\n RETURN jsonb_build_object('ok', false, 'error', 'name and channel_key are required');\n END IF;\n IF v_status NOT IN ('active', 'paused', 'ended', 'draft') THEN\n RETURN jsonb_build_object('ok', false, 'error', 'status must be one of active|paused|ended|draft');\n END IF;\n\n -- \u2500\u2500 authorization: role \u2192 plan \u2192 campaigns_active cap \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n SELECT count(*) INTO v_used FROM plg_marketing_campaigns c\n WHERE c.tenant_id = p_tenant_id AND c.status = 'active';\n v_denial := agent_guard(p_tenant_id, p_actor_user_id,\n 'marketing', 'create', 'campaigns_active', v_used,\n CASE WHEN v_status = 'active' THEN 1 ELSE 0 END);\n IF v_denial IS NOT NULL THEN\n RETURN jsonb_build_object('ok', false, 'denial', v_denial);\n END IF;\n\n -- \u2500\u2500 channel resolution (key or label, normalized) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n SELECT EXISTS (SELECT 1 FROM plg_marketing_channels ch WHERE ch.tenant_id = p_tenant_id)\n INTO v_has_channels;\n IF v_has_channels THEN\n SELECT ch.channel_key INTO v_channel_key\n FROM plg_marketing_channels ch\n WHERE ch.tenant_id = p_tenant_id\n AND (\n regexp_replace(lower(ch.channel_key), '[^a-z0-9]', '', 'g')\n = regexp_replace(lower(v_channel_raw), '[^a-z0-9]', '', 'g')\n OR regexp_replace(lower(ch.label), '[^a-z0-9]', '', 'g')\n = regexp_replace(lower(v_channel_raw), '[^a-z0-9]', '', 'g')\n )\n LIMIT 1;\n IF v_channel_key IS NULL THEN\n RETURN jsonb_build_object('ok', false, 'error',\n 'unknown channel \"' || v_channel_raw || '\" \u2014 valid channels: ' ||\n COALESCE((SELECT string_agg(ch.channel_key, ', ' ORDER BY ch.channel_key)\n FROM plg_marketing_channels ch WHERE ch.tenant_id = p_tenant_id), ''));\n END IF;\n ELSE\n v_channel_key := v_channel_raw;\n END IF;\n\n -- \u2500\u2500 write + audit \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n INSERT INTO plg_marketing_campaigns (tenant_id, name, channel_key, status, starts_at, ends_at, spend)\n VALUES (p_tenant_id, v_name, v_channel_key, v_status, COALESCE(v_starts, now()), v_ends, v_spend)\n RETURNING id INTO v_id;\n\n INSERT INTO audit_logs (tenant_id, user_id, action, entity_type, entity_id, metadata)\n VALUES (p_tenant_id, p_actor_user_id, 'agent.createCampaign', 'marketing_campaign', v_id::text,\n jsonb_build_object('payload', p_payload));\n\n RETURN jsonb_build_object(\n 'ok', true,\n 'id', v_id,\n 'record', jsonb_build_object(\n 'id', v_id,\n 'name', v_name,\n 'channel_key', v_channel_key,\n 'status', v_status,\n 'starts_at', COALESCE(v_starts, now()),\n 'spend', v_spend\n )\n );\nEND;\n$$;\n\nREVOKE ALL ON FUNCTION public.agent_marketing_create_campaign(uuid, uuid, jsonb) FROM public;\nREVOKE EXECUTE ON FUNCTION public.agent_marketing_create_campaign(uuid, uuid, jsonb) FROM anon;\nGRANT EXECUTE ON FUNCTION public.agent_marketing_create_campaign(uuid, uuid, jsonb)\n TO authenticated, service_role;\n";
6
9
  export declare const MIGRATIONS: Array<{
7
10
  id: string;
8
11
  sql: string;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/migrations/index.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,wBAAwB,i6BAepC,CAAA;AAED,eAAO,MAAM,6BAA6B,ivNA8FzC,CAAA;AAED,eAAO,MAAM,4BAA4B,inCAyBxC,CAAA;AAED,eAAO,MAAM,2BAA2B,8/CAyBvC,CAAA;AAED,eAAO,MAAM,uBAAuB,6tHAkDnC,CAAA;AAED,eAAO,MAAM,UAAU,EAAE,KAAK,CAAC;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAMzD,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/migrations/index.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,wBAAwB,i6BAepC,CAAA;AAED,eAAO,MAAM,6BAA6B,ivNA8FzC,CAAA;AAED,eAAO,MAAM,4BAA4B,inCAyBxC,CAAA;AAED,eAAO,MAAM,2BAA2B,8/CAyBvC,CAAA;AAED,eAAO,MAAM,uBAAuB,6tHAkDnC,CAAA;AAED,eAAO,MAAM,4BAA4B,k4GA4DxC,CAAA;AAED,eAAO,MAAM,6BAA6B,6rGAoEzC,CAAA;AAED,eAAO,MAAM,wBAAwB,qgMAuHpC,CAAA;AAED,eAAO,MAAM,UAAU,EAAE,KAAK,CAAC;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CASzD,CAAA"}
@@ -1,7 +1,7 @@
1
1
  import type { PluginRegistryDef } from '@fayz-ai/core';
2
2
  import type { AcquisitionChannel } from './types';
3
- /** Build the marketing settings registries, seeding Channels from the resolved
4
- * domain preset. Read-only today (channels come from the preset); flip
5
- * readOnly + add a `data.table` to make them tenant-editable. */
3
+ /** Build the marketing settings registries. Channels are tenant-editable rows
4
+ * in plg_marketing_channels; the Supabase provider lazily seeds them from the
5
+ * resolved domain preset (seedData doubles as walkable mock data). */
6
6
  export declare function buildMarketingRegistries(channels: AcquisitionChannel[]): PluginRegistryDef[];
7
7
  //# sourceMappingURL=registries.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"registries.d.ts","sourceRoot":"","sources":["../src/registries.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAa,MAAM,eAAe,CAAA;AACjE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAA;AA+BjD;;kEAEkE;AAClE,wBAAgB,wBAAwB,CAAC,QAAQ,EAAE,kBAAkB,EAAE,GAAG,iBAAiB,EAAE,CAY5F"}
1
+ {"version":3,"file":"registries.d.ts","sourceRoot":"","sources":["../src/registries.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAa,MAAM,eAAe,CAAA;AACjE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAA;AAwCjD;;uEAEuE;AACvE,wBAAgB,wBAAwB,CAAC,QAAQ,EAAE,kBAAkB,EAAE,GAAG,iBAAiB,EAAE,CAe5F"}
@@ -1,3 +1,357 @@
1
+ export declare const mktChannels: import("drizzle-orm/pg-core").PgTableWithColumns<{
2
+ name: "plg_marketing_channels";
3
+ schema: undefined;
4
+ columns: {
5
+ createdAt: import("drizzle-orm/pg-core").PgColumn<{
6
+ name: "created_at";
7
+ tableName: "plg_marketing_channels";
8
+ dataType: "date";
9
+ columnType: "PgTimestamp";
10
+ data: Date;
11
+ driverParam: string;
12
+ notNull: true;
13
+ hasDefault: true;
14
+ isPrimaryKey: false;
15
+ isAutoincrement: false;
16
+ hasRuntimeDefault: false;
17
+ enumValues: undefined;
18
+ baseColumn: never;
19
+ identity: undefined;
20
+ generated: undefined;
21
+ }, {}, {}>;
22
+ updatedAt: import("drizzle-orm/pg-core").PgColumn<{
23
+ name: "updated_at";
24
+ tableName: "plg_marketing_channels";
25
+ dataType: "date";
26
+ columnType: "PgTimestamp";
27
+ data: Date;
28
+ driverParam: string;
29
+ notNull: true;
30
+ hasDefault: true;
31
+ isPrimaryKey: false;
32
+ isAutoincrement: false;
33
+ hasRuntimeDefault: false;
34
+ enumValues: undefined;
35
+ baseColumn: never;
36
+ identity: undefined;
37
+ generated: undefined;
38
+ }, {}, {}>;
39
+ id: import("drizzle-orm/pg-core").PgColumn<{
40
+ name: "id";
41
+ tableName: "plg_marketing_channels";
42
+ dataType: "string";
43
+ columnType: "PgUUID";
44
+ data: string;
45
+ driverParam: string;
46
+ notNull: true;
47
+ hasDefault: true;
48
+ isPrimaryKey: true;
49
+ isAutoincrement: false;
50
+ hasRuntimeDefault: false;
51
+ enumValues: undefined;
52
+ baseColumn: never;
53
+ identity: undefined;
54
+ generated: undefined;
55
+ }, {}, {}>;
56
+ tenantId: import("drizzle-orm/pg-core").PgColumn<{
57
+ name: "tenant_id";
58
+ tableName: "plg_marketing_channels";
59
+ dataType: "string";
60
+ columnType: "PgUUID";
61
+ data: string;
62
+ driverParam: string;
63
+ notNull: true;
64
+ hasDefault: false;
65
+ isPrimaryKey: false;
66
+ isAutoincrement: false;
67
+ hasRuntimeDefault: false;
68
+ enumValues: undefined;
69
+ baseColumn: never;
70
+ identity: undefined;
71
+ generated: undefined;
72
+ }, {}, {}>;
73
+ channelKey: import("drizzle-orm/pg-core").PgColumn<{
74
+ name: "channel_key";
75
+ tableName: "plg_marketing_channels";
76
+ dataType: "string";
77
+ columnType: "PgText";
78
+ data: string;
79
+ driverParam: string;
80
+ notNull: true;
81
+ hasDefault: false;
82
+ isPrimaryKey: false;
83
+ isAutoincrement: false;
84
+ hasRuntimeDefault: false;
85
+ enumValues: [string, ...string[]];
86
+ baseColumn: never;
87
+ identity: undefined;
88
+ generated: undefined;
89
+ }, {}, {}>;
90
+ label: import("drizzle-orm/pg-core").PgColumn<{
91
+ name: "label";
92
+ tableName: "plg_marketing_channels";
93
+ dataType: "string";
94
+ columnType: "PgText";
95
+ data: string;
96
+ driverParam: string;
97
+ notNull: true;
98
+ hasDefault: false;
99
+ isPrimaryKey: false;
100
+ isAutoincrement: false;
101
+ hasRuntimeDefault: false;
102
+ enumValues: [string, ...string[]];
103
+ baseColumn: never;
104
+ identity: undefined;
105
+ generated: undefined;
106
+ }, {}, {}>;
107
+ icon: import("drizzle-orm/pg-core").PgColumn<{
108
+ name: "icon";
109
+ tableName: "plg_marketing_channels";
110
+ dataType: "string";
111
+ columnType: "PgText";
112
+ data: string;
113
+ driverParam: string;
114
+ notNull: false;
115
+ hasDefault: false;
116
+ isPrimaryKey: false;
117
+ isAutoincrement: false;
118
+ hasRuntimeDefault: false;
119
+ enumValues: [string, ...string[]];
120
+ baseColumn: never;
121
+ identity: undefined;
122
+ generated: undefined;
123
+ }, {}, {}>;
124
+ kind: import("drizzle-orm/pg-core").PgColumn<{
125
+ name: "kind";
126
+ tableName: "plg_marketing_channels";
127
+ dataType: "string";
128
+ columnType: "PgText";
129
+ data: string;
130
+ driverParam: string;
131
+ notNull: true;
132
+ hasDefault: true;
133
+ isPrimaryKey: false;
134
+ isAutoincrement: false;
135
+ hasRuntimeDefault: false;
136
+ enumValues: [string, ...string[]];
137
+ baseColumn: never;
138
+ identity: undefined;
139
+ generated: undefined;
140
+ }, {}, {}>;
141
+ isActive: import("drizzle-orm/pg-core").PgColumn<{
142
+ name: "is_active";
143
+ tableName: "plg_marketing_channels";
144
+ dataType: "boolean";
145
+ columnType: "PgBoolean";
146
+ data: boolean;
147
+ driverParam: boolean;
148
+ notNull: true;
149
+ hasDefault: true;
150
+ isPrimaryKey: false;
151
+ isAutoincrement: false;
152
+ hasRuntimeDefault: false;
153
+ enumValues: undefined;
154
+ baseColumn: never;
155
+ identity: undefined;
156
+ generated: undefined;
157
+ }, {}, {}>;
158
+ monthlySpend: import("drizzle-orm/pg-core").PgColumn<{
159
+ name: "monthly_spend";
160
+ tableName: "plg_marketing_channels";
161
+ dataType: "string";
162
+ columnType: "PgNumeric";
163
+ data: string;
164
+ driverParam: string;
165
+ notNull: true;
166
+ hasDefault: true;
167
+ isPrimaryKey: false;
168
+ isAutoincrement: false;
169
+ hasRuntimeDefault: false;
170
+ enumValues: undefined;
171
+ baseColumn: never;
172
+ identity: undefined;
173
+ generated: undefined;
174
+ }, {}, {}>;
175
+ };
176
+ dialect: "pg";
177
+ }>;
178
+ export declare const mktCampaigns: import("drizzle-orm/pg-core").PgTableWithColumns<{
179
+ name: "plg_marketing_campaigns";
180
+ schema: undefined;
181
+ columns: {
182
+ createdAt: import("drizzle-orm/pg-core").PgColumn<{
183
+ name: "created_at";
184
+ tableName: "plg_marketing_campaigns";
185
+ dataType: "date";
186
+ columnType: "PgTimestamp";
187
+ data: Date;
188
+ driverParam: string;
189
+ notNull: true;
190
+ hasDefault: true;
191
+ isPrimaryKey: false;
192
+ isAutoincrement: false;
193
+ hasRuntimeDefault: false;
194
+ enumValues: undefined;
195
+ baseColumn: never;
196
+ identity: undefined;
197
+ generated: undefined;
198
+ }, {}, {}>;
199
+ updatedAt: import("drizzle-orm/pg-core").PgColumn<{
200
+ name: "updated_at";
201
+ tableName: "plg_marketing_campaigns";
202
+ dataType: "date";
203
+ columnType: "PgTimestamp";
204
+ data: Date;
205
+ driverParam: string;
206
+ notNull: true;
207
+ hasDefault: true;
208
+ isPrimaryKey: false;
209
+ isAutoincrement: false;
210
+ hasRuntimeDefault: false;
211
+ enumValues: undefined;
212
+ baseColumn: never;
213
+ identity: undefined;
214
+ generated: undefined;
215
+ }, {}, {}>;
216
+ id: import("drizzle-orm/pg-core").PgColumn<{
217
+ name: "id";
218
+ tableName: "plg_marketing_campaigns";
219
+ dataType: "string";
220
+ columnType: "PgUUID";
221
+ data: string;
222
+ driverParam: string;
223
+ notNull: true;
224
+ hasDefault: true;
225
+ isPrimaryKey: true;
226
+ isAutoincrement: false;
227
+ hasRuntimeDefault: false;
228
+ enumValues: undefined;
229
+ baseColumn: never;
230
+ identity: undefined;
231
+ generated: undefined;
232
+ }, {}, {}>;
233
+ tenantId: import("drizzle-orm/pg-core").PgColumn<{
234
+ name: "tenant_id";
235
+ tableName: "plg_marketing_campaigns";
236
+ dataType: "string";
237
+ columnType: "PgUUID";
238
+ data: string;
239
+ driverParam: string;
240
+ notNull: true;
241
+ hasDefault: false;
242
+ isPrimaryKey: false;
243
+ isAutoincrement: false;
244
+ hasRuntimeDefault: false;
245
+ enumValues: undefined;
246
+ baseColumn: never;
247
+ identity: undefined;
248
+ generated: undefined;
249
+ }, {}, {}>;
250
+ name: import("drizzle-orm/pg-core").PgColumn<{
251
+ name: "name";
252
+ tableName: "plg_marketing_campaigns";
253
+ dataType: "string";
254
+ columnType: "PgText";
255
+ data: string;
256
+ driverParam: string;
257
+ notNull: true;
258
+ hasDefault: false;
259
+ isPrimaryKey: false;
260
+ isAutoincrement: false;
261
+ hasRuntimeDefault: false;
262
+ enumValues: [string, ...string[]];
263
+ baseColumn: never;
264
+ identity: undefined;
265
+ generated: undefined;
266
+ }, {}, {}>;
267
+ channelKey: import("drizzle-orm/pg-core").PgColumn<{
268
+ name: "channel_key";
269
+ tableName: "plg_marketing_campaigns";
270
+ dataType: "string";
271
+ columnType: "PgText";
272
+ data: string;
273
+ driverParam: string;
274
+ notNull: true;
275
+ hasDefault: false;
276
+ isPrimaryKey: false;
277
+ isAutoincrement: false;
278
+ hasRuntimeDefault: false;
279
+ enumValues: [string, ...string[]];
280
+ baseColumn: never;
281
+ identity: undefined;
282
+ generated: undefined;
283
+ }, {}, {}>;
284
+ status: import("drizzle-orm/pg-core").PgColumn<{
285
+ name: "status";
286
+ tableName: "plg_marketing_campaigns";
287
+ dataType: "string";
288
+ columnType: "PgText";
289
+ data: string;
290
+ driverParam: string;
291
+ notNull: true;
292
+ hasDefault: true;
293
+ isPrimaryKey: false;
294
+ isAutoincrement: false;
295
+ hasRuntimeDefault: false;
296
+ enumValues: [string, ...string[]];
297
+ baseColumn: never;
298
+ identity: undefined;
299
+ generated: undefined;
300
+ }, {}, {}>;
301
+ startsAt: import("drizzle-orm/pg-core").PgColumn<{
302
+ name: "starts_at";
303
+ tableName: "plg_marketing_campaigns";
304
+ dataType: "date";
305
+ columnType: "PgTimestamp";
306
+ data: Date;
307
+ driverParam: string;
308
+ notNull: false;
309
+ hasDefault: false;
310
+ isPrimaryKey: false;
311
+ isAutoincrement: false;
312
+ hasRuntimeDefault: false;
313
+ enumValues: undefined;
314
+ baseColumn: never;
315
+ identity: undefined;
316
+ generated: undefined;
317
+ }, {}, {}>;
318
+ endsAt: import("drizzle-orm/pg-core").PgColumn<{
319
+ name: "ends_at";
320
+ tableName: "plg_marketing_campaigns";
321
+ dataType: "date";
322
+ columnType: "PgTimestamp";
323
+ data: Date;
324
+ driverParam: string;
325
+ notNull: false;
326
+ hasDefault: false;
327
+ isPrimaryKey: false;
328
+ isAutoincrement: false;
329
+ hasRuntimeDefault: false;
330
+ enumValues: undefined;
331
+ baseColumn: never;
332
+ identity: undefined;
333
+ generated: undefined;
334
+ }, {}, {}>;
335
+ spend: import("drizzle-orm/pg-core").PgColumn<{
336
+ name: "spend";
337
+ tableName: "plg_marketing_campaigns";
338
+ dataType: "string";
339
+ columnType: "PgNumeric";
340
+ data: string;
341
+ driverParam: string;
342
+ notNull: true;
343
+ hasDefault: true;
344
+ isPrimaryKey: false;
345
+ isAutoincrement: false;
346
+ hasRuntimeDefault: false;
347
+ enumValues: undefined;
348
+ baseColumn: never;
349
+ identity: undefined;
350
+ generated: undefined;
351
+ }, {}, {}>;
352
+ };
353
+ dialect: "pg";
354
+ }>;
1
355
  export declare const mktSocialAccounts: import("drizzle-orm/pg-core").PgTableWithColumns<{
2
356
  name: "plg_marketing_social_accounts";
3
357
  schema: undefined;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/schema/index.ts"],"names":[],"mappings":"AAaA,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAS+C,CAAA;AAE7E,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAkB1B,CAAA;AAEF,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAwB1B,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/schema/index.ts"],"names":[],"mappings":"AAiBA,eAAO,MAAM,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAatB,CAAA;AAKF,eAAO,MAAM,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAavB,CAAA;AAEF,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAS+C,CAAA;AAE7E,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAkB1B,CAAA;AAEF,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAwB1B,CAAA"}
package/dist/schema.cjs CHANGED
@@ -3,6 +3,34 @@
3
3
  var db = require('@fayz-ai/db');
4
4
 
5
5
  // src/schema/index.ts
6
+ var mktChannels = db.pgTable("plg_marketing_channels", {
7
+ id: db.uuid("id").primaryKey().defaultRandom(),
8
+ tenantId: db.tenantId(),
9
+ channelKey: db.text("channel_key").notNull(),
10
+ label: db.text("label").notNull(),
11
+ icon: db.text("icon"),
12
+ kind: db.text("kind").notNull().default("organic"),
13
+ isActive: db.boolean("is_active").notNull().default(true),
14
+ monthlySpend: db.numeric("monthly_spend").notNull().default("0"),
15
+ ...db.timestamps
16
+ }, (t) => [
17
+ db.index("idx_plg_marketing_channels_tenant").on(t.tenantId),
18
+ db.uniqueIndex("u_plg_marketing_channels_tenant_key").on(t.tenantId, t.channelKey)
19
+ ]);
20
+ var mktCampaigns = db.pgTable("plg_marketing_campaigns", {
21
+ id: db.uuid("id").primaryKey().defaultRandom(),
22
+ tenantId: db.tenantId(),
23
+ name: db.text("name").notNull(),
24
+ channelKey: db.text("channel_key").notNull(),
25
+ status: db.text("status").notNull().default("draft"),
26
+ startsAt: db.timestamp("starts_at", { withTimezone: true }),
27
+ endsAt: db.timestamp("ends_at", { withTimezone: true }),
28
+ spend: db.numeric("spend").notNull().default("0"),
29
+ ...db.timestamps
30
+ }, (t) => [
31
+ db.index("idx_plg_marketing_campaigns_tenant").on(t.tenantId),
32
+ db.index("idx_plg_marketing_campaigns_channel").on(t.tenantId, t.channelKey)
33
+ ]);
6
34
  var mktSocialAccounts = db.pgTable("plg_marketing_social_accounts", {
7
35
  id: db.uuid("id").primaryKey().defaultRandom(),
8
36
  tenantId: db.tenantId(),
@@ -58,6 +86,8 @@ var mktContentPosts = db.pgTable("plg_marketing_content_posts", {
58
86
  db.index("idx_plg_marketing_content_posts_status").on(t.tenantId, t.status)
59
87
  ]);
60
88
 
89
+ exports.mktCampaigns = mktCampaigns;
90
+ exports.mktChannels = mktChannels;
61
91
  exports.mktContentPlans = mktContentPlans;
62
92
  exports.mktContentPosts = mktContentPosts;
63
93
  exports.mktSocialAccounts = mktSocialAccounts;
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/schema/index.ts"],"names":["pgTable","uuid","tenantId","text","boolean","createdAt","index","integer","date","jsonb","timestamps"],"mappings":"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","file":"schema.cjs","sourcesContent":["// Builders come from @fayz-ai/db (single drizzle-orm instance) — never import\n// pgTable/uuid directly from drizzle-orm/pg-core. See MIGRATION-ARCHITECTURE §6.\nimport {\n pgTable, uuid, text, boolean, integer, date, jsonb, index,\n tenantId, timestamps, createdAt,\n} from '@fayz-ai/db'\n\n// ---------------------------------------------------------------------------\n// Marketing plugin — Ring-1 schema (schema-as-code), content planner tables.\n// RLS policies + grants are companion SQL (Drizzle doesn't diff those) —\n// see src/migrations/001_content_planner.sql.\n// ---------------------------------------------------------------------------\n\nexport const mktSocialAccounts = pgTable('plg_marketing_social_accounts', {\n id: uuid('id').primaryKey().defaultRandom(),\n tenantId: tenantId(),\n name: text('name').notNull(),\n handle: text('handle'),\n // One account, many platform connections (Instagram, YouTube, TikTok, ...)\n platforms: text('platforms').array().notNull().default(['instagram']),\n isActive: boolean('is_active').notNull().default(true),\n ...createdAt,\n}, (t) => [index('idx_plg_marketing_social_accounts_tenant').on(t.tenantId)])\n\nexport const mktContentPlans = pgTable('plg_marketing_content_plans', {\n id: uuid('id').primaryKey().defaultRandom(),\n tenantId: tenantId(),\n accountId: uuid('account_id').notNull().references(() => mktSocialAccounts.id, { onDelete: 'cascade' }),\n name: text('name').notNull(),\n status: text('status').notNull().default('active'),\n weeksCount: integer('weeks_count').notNull().default(8),\n startDate: date('start_date'),\n objective: text('objective'),\n tone: text('tone'),\n pillars: jsonb('pillars').notNull().default([]),\n formats: jsonb('formats').notNull().default([]),\n weeklyFrequency: integer('weekly_frequency').notNull().default(3),\n briefMd: text('brief_md').notNull().default(''),\n ...timestamps,\n}, (t) => [\n index('idx_plg_marketing_content_plans_tenant').on(t.tenantId),\n index('idx_plg_marketing_content_plans_account').on(t.accountId),\n])\n\nexport const mktContentPosts = pgTable('plg_marketing_content_posts', {\n id: uuid('id').primaryKey().defaultRandom(),\n tenantId: tenantId(),\n planId: uuid('plan_id').notNull().references(() => mktContentPlans.id, { onDelete: 'cascade' }),\n weekNumber: integer('week_number').notNull().default(1),\n position: integer('position').notNull().default(0),\n title: text('title').notNull(),\n format: text('format').notNull().default('reel'),\n status: text('status').notNull().default('idea'),\n scheduledDate: date('scheduled_date'),\n // Target platforms for this post; empty = inherit the account's platforms\n platforms: text('platforms').array().notNull().default([]),\n // Recording-day checklist [{ id, text, done }]\n checklist: jsonb('checklist').notNull().default([]),\n // Uploaded asset (static posts: caption + final art instead of a script)\n mediaUrl: text('media_url'),\n hook: text('hook'),\n cta: text('cta'),\n contentMd: text('content_md').notNull().default(''),\n ...timestamps,\n}, (t) => [\n index('idx_plg_marketing_content_posts_tenant').on(t.tenantId),\n index('idx_plg_marketing_content_posts_plan').on(t.planId, t.weekNumber, t.position),\n index('idx_plg_marketing_content_posts_status').on(t.tenantId, t.status),\n])\n"]}
1
+ {"version":3,"sources":["../src/schema/index.ts"],"names":["pgTable","uuid","tenantId","text","boolean","numeric","timestamps","index","uniqueIndex","timestamp","createdAt","integer","date","jsonb"],"mappings":"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","file":"schema.cjs","sourcesContent":["// Builders come from @fayz-ai/db (single drizzle-orm instance) — never import\n// pgTable/uuid directly from drizzle-orm/pg-core. See MIGRATION-ARCHITECTURE §6.\nimport {\n pgTable, uuid, text, boolean, integer, date, jsonb, index, uniqueIndex,\n numeric, timestamp,\n tenantId, timestamps, createdAt,\n} from '@fayz-ai/db'\n\n// ---------------------------------------------------------------------------\n// Marketing plugin — Ring-1 schema (schema-as-code), content planner tables.\n// RLS policies + grants are companion SQL (Drizzle doesn't diff those) —\n// see src/migrations/001_content_planner.sql.\n// ---------------------------------------------------------------------------\n\n// Acquisition channels — tenant-owned rows seeded from the domain preset.\n// `channel_key` is the stable preset/config id ('instagram', 'paid-search'…),\n// aligned with plugin-crm's lead-sources where they overlap.\nexport const mktChannels = pgTable('plg_marketing_channels', {\n id: uuid('id').primaryKey().defaultRandom(),\n tenantId: tenantId(),\n channelKey: text('channel_key').notNull(),\n label: text('label').notNull(),\n icon: text('icon'),\n kind: text('kind').notNull().default('organic'),\n isActive: boolean('is_active').notNull().default(true),\n monthlySpend: numeric('monthly_spend').notNull().default('0'),\n ...timestamps,\n}, (t) => [\n index('idx_plg_marketing_channels_tenant').on(t.tenantId),\n uniqueIndex('u_plg_marketing_channels_tenant_key').on(t.tenantId, t.channelKey),\n])\n\n// Acquisition campaigns — real CRUD; performance is DERIVED at read time from\n// v_marketing_attribution (channel match within the campaign window), only\n// spend is stored.\nexport const mktCampaigns = pgTable('plg_marketing_campaigns', {\n id: uuid('id').primaryKey().defaultRandom(),\n tenantId: tenantId(),\n name: text('name').notNull(),\n channelKey: text('channel_key').notNull(),\n status: text('status').notNull().default('draft'),\n startsAt: timestamp('starts_at', { withTimezone: true }),\n endsAt: timestamp('ends_at', { withTimezone: true }),\n spend: numeric('spend').notNull().default('0'),\n ...timestamps,\n}, (t) => [\n index('idx_plg_marketing_campaigns_tenant').on(t.tenantId),\n index('idx_plg_marketing_campaigns_channel').on(t.tenantId, t.channelKey),\n])\n\nexport const mktSocialAccounts = pgTable('plg_marketing_social_accounts', {\n id: uuid('id').primaryKey().defaultRandom(),\n tenantId: tenantId(),\n name: text('name').notNull(),\n handle: text('handle'),\n // One account, many platform connections (Instagram, YouTube, TikTok, ...)\n platforms: text('platforms').array().notNull().default(['instagram']),\n isActive: boolean('is_active').notNull().default(true),\n ...createdAt,\n}, (t) => [index('idx_plg_marketing_social_accounts_tenant').on(t.tenantId)])\n\nexport const mktContentPlans = pgTable('plg_marketing_content_plans', {\n id: uuid('id').primaryKey().defaultRandom(),\n tenantId: tenantId(),\n accountId: uuid('account_id').notNull().references(() => mktSocialAccounts.id, { onDelete: 'cascade' }),\n name: text('name').notNull(),\n status: text('status').notNull().default('active'),\n weeksCount: integer('weeks_count').notNull().default(8),\n startDate: date('start_date'),\n objective: text('objective'),\n tone: text('tone'),\n pillars: jsonb('pillars').notNull().default([]),\n formats: jsonb('formats').notNull().default([]),\n weeklyFrequency: integer('weekly_frequency').notNull().default(3),\n briefMd: text('brief_md').notNull().default(''),\n ...timestamps,\n}, (t) => [\n index('idx_plg_marketing_content_plans_tenant').on(t.tenantId),\n index('idx_plg_marketing_content_plans_account').on(t.accountId),\n])\n\nexport const mktContentPosts = pgTable('plg_marketing_content_posts', {\n id: uuid('id').primaryKey().defaultRandom(),\n tenantId: tenantId(),\n planId: uuid('plan_id').notNull().references(() => mktContentPlans.id, { onDelete: 'cascade' }),\n weekNumber: integer('week_number').notNull().default(1),\n position: integer('position').notNull().default(0),\n title: text('title').notNull(),\n format: text('format').notNull().default('reel'),\n status: text('status').notNull().default('idea'),\n scheduledDate: date('scheduled_date'),\n // Target platforms for this post; empty = inherit the account's platforms\n platforms: text('platforms').array().notNull().default([]),\n // Recording-day checklist [{ id, text, done }]\n checklist: jsonb('checklist').notNull().default([]),\n // Uploaded asset (static posts: caption + final art instead of a script)\n mediaUrl: text('media_url'),\n hook: text('hook'),\n cta: text('cta'),\n contentMd: text('content_md').notNull().default(''),\n ...timestamps,\n}, (t) => [\n index('idx_plg_marketing_content_posts_tenant').on(t.tenantId),\n index('idx_plg_marketing_content_posts_plan').on(t.planId, t.weekNumber, t.position),\n index('idx_plg_marketing_content_posts_status').on(t.tenantId, t.status),\n])\n"]}
package/dist/schema.js CHANGED
@@ -1,6 +1,34 @@
1
- import { pgTable, createdAt, boolean, text, tenantId, uuid, index, timestamps, integer, jsonb, date } from '@fayz-ai/db';
1
+ import { pgTable, timestamps, numeric, boolean, text, tenantId, uuid, index, uniqueIndex, timestamp, createdAt, integer, jsonb, date } from '@fayz-ai/db';
2
2
 
3
3
  // src/schema/index.ts
4
+ var mktChannels = pgTable("plg_marketing_channels", {
5
+ id: uuid("id").primaryKey().defaultRandom(),
6
+ tenantId: tenantId(),
7
+ channelKey: text("channel_key").notNull(),
8
+ label: text("label").notNull(),
9
+ icon: text("icon"),
10
+ kind: text("kind").notNull().default("organic"),
11
+ isActive: boolean("is_active").notNull().default(true),
12
+ monthlySpend: numeric("monthly_spend").notNull().default("0"),
13
+ ...timestamps
14
+ }, (t) => [
15
+ index("idx_plg_marketing_channels_tenant").on(t.tenantId),
16
+ uniqueIndex("u_plg_marketing_channels_tenant_key").on(t.tenantId, t.channelKey)
17
+ ]);
18
+ var mktCampaigns = pgTable("plg_marketing_campaigns", {
19
+ id: uuid("id").primaryKey().defaultRandom(),
20
+ tenantId: tenantId(),
21
+ name: text("name").notNull(),
22
+ channelKey: text("channel_key").notNull(),
23
+ status: text("status").notNull().default("draft"),
24
+ startsAt: timestamp("starts_at", { withTimezone: true }),
25
+ endsAt: timestamp("ends_at", { withTimezone: true }),
26
+ spend: numeric("spend").notNull().default("0"),
27
+ ...timestamps
28
+ }, (t) => [
29
+ index("idx_plg_marketing_campaigns_tenant").on(t.tenantId),
30
+ index("idx_plg_marketing_campaigns_channel").on(t.tenantId, t.channelKey)
31
+ ]);
4
32
  var mktSocialAccounts = pgTable("plg_marketing_social_accounts", {
5
33
  id: uuid("id").primaryKey().defaultRandom(),
6
34
  tenantId: tenantId(),
@@ -56,6 +84,6 @@ var mktContentPosts = pgTable("plg_marketing_content_posts", {
56
84
  index("idx_plg_marketing_content_posts_status").on(t.tenantId, t.status)
57
85
  ]);
58
86
 
59
- export { mktContentPlans, mktContentPosts, mktSocialAccounts };
87
+ export { mktCampaigns, mktChannels, mktContentPlans, mktContentPosts, mktSocialAccounts };
60
88
  //# sourceMappingURL=schema.js.map
61
89
  //# sourceMappingURL=schema.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/schema/index.ts"],"names":[],"mappings":"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","file":"schema.js","sourcesContent":["// Builders come from @fayz-ai/db (single drizzle-orm instance) — never import\n// pgTable/uuid directly from drizzle-orm/pg-core. See MIGRATION-ARCHITECTURE §6.\nimport {\n pgTable, uuid, text, boolean, integer, date, jsonb, index,\n tenantId, timestamps, createdAt,\n} from '@fayz-ai/db'\n\n// ---------------------------------------------------------------------------\n// Marketing plugin — Ring-1 schema (schema-as-code), content planner tables.\n// RLS policies + grants are companion SQL (Drizzle doesn't diff those) —\n// see src/migrations/001_content_planner.sql.\n// ---------------------------------------------------------------------------\n\nexport const mktSocialAccounts = pgTable('plg_marketing_social_accounts', {\n id: uuid('id').primaryKey().defaultRandom(),\n tenantId: tenantId(),\n name: text('name').notNull(),\n handle: text('handle'),\n // One account, many platform connections (Instagram, YouTube, TikTok, ...)\n platforms: text('platforms').array().notNull().default(['instagram']),\n isActive: boolean('is_active').notNull().default(true),\n ...createdAt,\n}, (t) => [index('idx_plg_marketing_social_accounts_tenant').on(t.tenantId)])\n\nexport const mktContentPlans = pgTable('plg_marketing_content_plans', {\n id: uuid('id').primaryKey().defaultRandom(),\n tenantId: tenantId(),\n accountId: uuid('account_id').notNull().references(() => mktSocialAccounts.id, { onDelete: 'cascade' }),\n name: text('name').notNull(),\n status: text('status').notNull().default('active'),\n weeksCount: integer('weeks_count').notNull().default(8),\n startDate: date('start_date'),\n objective: text('objective'),\n tone: text('tone'),\n pillars: jsonb('pillars').notNull().default([]),\n formats: jsonb('formats').notNull().default([]),\n weeklyFrequency: integer('weekly_frequency').notNull().default(3),\n briefMd: text('brief_md').notNull().default(''),\n ...timestamps,\n}, (t) => [\n index('idx_plg_marketing_content_plans_tenant').on(t.tenantId),\n index('idx_plg_marketing_content_plans_account').on(t.accountId),\n])\n\nexport const mktContentPosts = pgTable('plg_marketing_content_posts', {\n id: uuid('id').primaryKey().defaultRandom(),\n tenantId: tenantId(),\n planId: uuid('plan_id').notNull().references(() => mktContentPlans.id, { onDelete: 'cascade' }),\n weekNumber: integer('week_number').notNull().default(1),\n position: integer('position').notNull().default(0),\n title: text('title').notNull(),\n format: text('format').notNull().default('reel'),\n status: text('status').notNull().default('idea'),\n scheduledDate: date('scheduled_date'),\n // Target platforms for this post; empty = inherit the account's platforms\n platforms: text('platforms').array().notNull().default([]),\n // Recording-day checklist [{ id, text, done }]\n checklist: jsonb('checklist').notNull().default([]),\n // Uploaded asset (static posts: caption + final art instead of a script)\n mediaUrl: text('media_url'),\n hook: text('hook'),\n cta: text('cta'),\n contentMd: text('content_md').notNull().default(''),\n ...timestamps,\n}, (t) => [\n index('idx_plg_marketing_content_posts_tenant').on(t.tenantId),\n index('idx_plg_marketing_content_posts_plan').on(t.planId, t.weekNumber, t.position),\n index('idx_plg_marketing_content_posts_status').on(t.tenantId, t.status),\n])\n"]}
1
+ {"version":3,"sources":["../src/schema/index.ts"],"names":[],"mappings":"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","file":"schema.js","sourcesContent":["// Builders come from @fayz-ai/db (single drizzle-orm instance) — never import\n// pgTable/uuid directly from drizzle-orm/pg-core. See MIGRATION-ARCHITECTURE §6.\nimport {\n pgTable, uuid, text, boolean, integer, date, jsonb, index, uniqueIndex,\n numeric, timestamp,\n tenantId, timestamps, createdAt,\n} from '@fayz-ai/db'\n\n// ---------------------------------------------------------------------------\n// Marketing plugin — Ring-1 schema (schema-as-code), content planner tables.\n// RLS policies + grants are companion SQL (Drizzle doesn't diff those) —\n// see src/migrations/001_content_planner.sql.\n// ---------------------------------------------------------------------------\n\n// Acquisition channels — tenant-owned rows seeded from the domain preset.\n// `channel_key` is the stable preset/config id ('instagram', 'paid-search'…),\n// aligned with plugin-crm's lead-sources where they overlap.\nexport const mktChannels = pgTable('plg_marketing_channels', {\n id: uuid('id').primaryKey().defaultRandom(),\n tenantId: tenantId(),\n channelKey: text('channel_key').notNull(),\n label: text('label').notNull(),\n icon: text('icon'),\n kind: text('kind').notNull().default('organic'),\n isActive: boolean('is_active').notNull().default(true),\n monthlySpend: numeric('monthly_spend').notNull().default('0'),\n ...timestamps,\n}, (t) => [\n index('idx_plg_marketing_channels_tenant').on(t.tenantId),\n uniqueIndex('u_plg_marketing_channels_tenant_key').on(t.tenantId, t.channelKey),\n])\n\n// Acquisition campaigns — real CRUD; performance is DERIVED at read time from\n// v_marketing_attribution (channel match within the campaign window), only\n// spend is stored.\nexport const mktCampaigns = pgTable('plg_marketing_campaigns', {\n id: uuid('id').primaryKey().defaultRandom(),\n tenantId: tenantId(),\n name: text('name').notNull(),\n channelKey: text('channel_key').notNull(),\n status: text('status').notNull().default('draft'),\n startsAt: timestamp('starts_at', { withTimezone: true }),\n endsAt: timestamp('ends_at', { withTimezone: true }),\n spend: numeric('spend').notNull().default('0'),\n ...timestamps,\n}, (t) => [\n index('idx_plg_marketing_campaigns_tenant').on(t.tenantId),\n index('idx_plg_marketing_campaigns_channel').on(t.tenantId, t.channelKey),\n])\n\nexport const mktSocialAccounts = pgTable('plg_marketing_social_accounts', {\n id: uuid('id').primaryKey().defaultRandom(),\n tenantId: tenantId(),\n name: text('name').notNull(),\n handle: text('handle'),\n // One account, many platform connections (Instagram, YouTube, TikTok, ...)\n platforms: text('platforms').array().notNull().default(['instagram']),\n isActive: boolean('is_active').notNull().default(true),\n ...createdAt,\n}, (t) => [index('idx_plg_marketing_social_accounts_tenant').on(t.tenantId)])\n\nexport const mktContentPlans = pgTable('plg_marketing_content_plans', {\n id: uuid('id').primaryKey().defaultRandom(),\n tenantId: tenantId(),\n accountId: uuid('account_id').notNull().references(() => mktSocialAccounts.id, { onDelete: 'cascade' }),\n name: text('name').notNull(),\n status: text('status').notNull().default('active'),\n weeksCount: integer('weeks_count').notNull().default(8),\n startDate: date('start_date'),\n objective: text('objective'),\n tone: text('tone'),\n pillars: jsonb('pillars').notNull().default([]),\n formats: jsonb('formats').notNull().default([]),\n weeklyFrequency: integer('weekly_frequency').notNull().default(3),\n briefMd: text('brief_md').notNull().default(''),\n ...timestamps,\n}, (t) => [\n index('idx_plg_marketing_content_plans_tenant').on(t.tenantId),\n index('idx_plg_marketing_content_plans_account').on(t.accountId),\n])\n\nexport const mktContentPosts = pgTable('plg_marketing_content_posts', {\n id: uuid('id').primaryKey().defaultRandom(),\n tenantId: tenantId(),\n planId: uuid('plan_id').notNull().references(() => mktContentPlans.id, { onDelete: 'cascade' }),\n weekNumber: integer('week_number').notNull().default(1),\n position: integer('position').notNull().default(0),\n title: text('title').notNull(),\n format: text('format').notNull().default('reel'),\n status: text('status').notNull().default('idea'),\n scheduledDate: date('scheduled_date'),\n // Target platforms for this post; empty = inherit the account's platforms\n platforms: text('platforms').array().notNull().default([]),\n // Recording-day checklist [{ id, text, done }]\n checklist: jsonb('checklist').notNull().default([]),\n // Uploaded asset (static posts: caption + final art instead of a script)\n mediaUrl: text('media_url'),\n hook: text('hook'),\n cta: text('cta'),\n contentMd: text('content_md').notNull().default(''),\n ...timestamps,\n}, (t) => [\n index('idx_plg_marketing_content_posts_tenant').on(t.tenantId),\n index('idx_plg_marketing_content_posts_plan').on(t.planId, t.weekNumber, t.position),\n index('idx_plg_marketing_content_posts_status').on(t.tenantId, t.status),\n])\n"]}
package/dist/store.d.ts CHANGED
@@ -10,6 +10,8 @@ export interface MarketingUIState {
10
10
  landingPages: LandingPagePerf[];
11
11
  loading: boolean;
12
12
  load(): Promise<void>;
13
+ /** Invalidate the provider cache and reload (external writes, e.g. agent RPCs). */
14
+ refresh(): Promise<void>;
13
15
  setRange(range: DateRangeKey): Promise<void>;
14
16
  saveCampaign(input: SaveCampaignInput): Promise<Campaign>;
15
17
  deleteCampaign(id: string): Promise<void>;
@@ -1 +1 @@
1
- {"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../src/store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAe,KAAK,QAAQ,EAAE,MAAM,iBAAiB,CAAA;AAC5D,OAAO,KAAK,EAAE,qBAAqB,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAA;AAC5E,OAAO,KAAK,EACV,QAAQ,EACR,kBAAkB,EAClB,YAAY,EACZ,WAAW,EACX,eAAe,EACf,iBAAiB,EAClB,MAAM,SAAS,CAAA;AAEhB,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,YAAY,CAAA;IACnB,QAAQ,EAAE,iBAAiB,GAAG,IAAI,CAAA;IAClC,QAAQ,EAAE,kBAAkB,EAAE,CAAA;IAC9B,SAAS,EAAE,QAAQ,EAAE,CAAA;IACrB,MAAM,EAAE,WAAW,EAAE,CAAA;IACrB,YAAY,EAAE,eAAe,EAAE,CAAA;IAC/B,OAAO,EAAE,OAAO,CAAA;IAEhB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;IACrB,QAAQ,CAAC,KAAK,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAC5C,YAAY,CAAC,KAAK,EAAE,iBAAiB,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAA;IACzD,cAAc,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;CAC1C;AAED,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,qBAAqB,GAAG,QAAQ,CAAC,gBAAgB,CAAC,CAuChG"}
1
+ {"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../src/store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAe,KAAK,QAAQ,EAAE,MAAM,iBAAiB,CAAA;AAC5D,OAAO,KAAK,EAAE,qBAAqB,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAA;AAC5E,OAAO,KAAK,EACV,QAAQ,EACR,kBAAkB,EAClB,YAAY,EACZ,WAAW,EACX,eAAe,EACf,iBAAiB,EAClB,MAAM,SAAS,CAAA;AAEhB,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,YAAY,CAAA;IACnB,QAAQ,EAAE,iBAAiB,GAAG,IAAI,CAAA;IAClC,QAAQ,EAAE,kBAAkB,EAAE,CAAA;IAC9B,SAAS,EAAE,QAAQ,EAAE,CAAA;IACrB,MAAM,EAAE,WAAW,EAAE,CAAA;IACrB,YAAY,EAAE,eAAe,EAAE,CAAA;IAC/B,OAAO,EAAE,OAAO,CAAA;IAEhB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;IACrB,mFAAmF;IACnF,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;IACxB,QAAQ,CAAC,KAAK,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAC5C,YAAY,CAAC,KAAK,EAAE,iBAAiB,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAA;IACzD,cAAc,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;CAC1C;AAED,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,qBAAqB,GAAG,QAAQ,CAAC,gBAAgB,CAAC,CAkDhG"}
@@ -1 +1 @@
1
- {"version":3,"file":"CampaignsView.d.ts","sourceRoot":"","sources":["../../src/views/CampaignsView.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAWzB,wBAAgB,aAAa,sBA4C5B"}
1
+ {"version":3,"file":"CampaignsView.d.ts","sourceRoot":"","sources":["../../src/views/CampaignsView.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAYzB,wBAAgB,aAAa,sBA8C5B"}
@@ -1 +1 @@
1
- {"version":3,"file":"BlogEntityForm.d.ts","sourceRoot":"","sources":["../../../src/views/blog/BlogEntityForm.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAGzB,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,eAAe,CAAA;AAU9C,wBAAgB,cAAc,CAAC,CAAC,SAAS;IAAE,EAAE,EAAE,MAAM,CAAA;CAAE,EAAE,EACvD,MAAM,EACN,MAAM,EACN,WAAW,EACX,MAAM,GACP,EAAE;IACD,MAAM,EAAE,SAAS,CAAC,CAAC,CAAC,CAAA;IACpB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,WAAW,EAAE,MAAM,CAAA;IACnB,MAAM,EAAE,MAAM,IAAI,CAAA;CACnB,qBAoEA"}
1
+ {"version":3,"file":"BlogEntityForm.d.ts","sourceRoot":"","sources":["../../../src/views/blog/BlogEntityForm.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAGzB,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,eAAe,CAAA;AAW9C,wBAAgB,cAAc,CAAC,CAAC,SAAS;IAAE,EAAE,EAAE,MAAM,CAAA;CAAE,EAAE,EACvD,MAAM,EACN,MAAM,EACN,WAAW,EACX,MAAM,GACP,EAAE;IACD,MAAM,EAAE,SAAS,CAAC,CAAC,CAAC,CAAA;IACpB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,WAAW,EAAE,MAAM,CAAA;IACnB,MAAM,EAAE,MAAM,IAAI,CAAA;CACnB,qBA4EA"}