@fayz-ai/plugin-marketing 0.9.0 → 0.9.1

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 (88) hide show
  1. package/README.md +3 -3
  2. package/dist/{MarketingContext.d.ts → context.d.ts} +5 -3
  3. package/dist/context.d.ts.map +1 -0
  4. package/dist/data/mock.d.ts +1 -1
  5. package/dist/data/mock.d.ts.map +1 -1
  6. package/dist/{registries.d.ts → data/registries.d.ts} +1 -1
  7. package/dist/data/registries.d.ts.map +1 -0
  8. package/dist/index.d.ts +4 -4
  9. package/dist/index.d.ts.map +1 -1
  10. package/dist/index.js +17 -22
  11. package/dist/index.js.map +1 -1
  12. package/dist/lib/format.d.ts.map +1 -0
  13. package/dist/{presets.d.ts → lib/presets.d.ts} +1 -1
  14. package/dist/lib/presets.d.ts.map +1 -0
  15. package/dist/{MarketingPage.d.ts → views/MarketingPage.d.ts} +5 -5
  16. package/dist/views/MarketingPage.d.ts.map +1 -0
  17. package/dist/views/blog/BlogAdminView.d.ts.map +1 -1
  18. package/dist/views/content/ContentPlannerContext.d.ts +1 -1
  19. package/dist/views/content/ContentPlannerContext.d.ts.map +1 -1
  20. package/dist/views/dashboardWidgets.d.ts +1 -1
  21. package/dist/views/dashboardWidgets.d.ts.map +1 -1
  22. package/package.json +6 -9
  23. package/dist/MarketingContext.d.ts.map +0 -1
  24. package/dist/MarketingPage.d.ts.map +0 -1
  25. package/dist/format.d.ts.map +0 -1
  26. package/dist/presets.d.ts.map +0 -1
  27. package/dist/registries.d.ts.map +0 -1
  28. package/src/MarketingContext.tsx +0 -42
  29. package/src/MarketingPage.tsx +0 -139
  30. package/src/components/MarketingBits.tsx +0 -131
  31. package/src/components/icons.tsx +0 -36
  32. package/src/data/blogEntities.tsx +0 -91
  33. package/src/data/capability.test.ts +0 -92
  34. package/src/data/contentMedia.ts +0 -25
  35. package/src/data/contentMock.ts +0 -253
  36. package/src/data/contentSupabase.ts +0 -263
  37. package/src/data/contentTypes.ts +0 -138
  38. package/src/data/mock.ts +0 -308
  39. package/src/data/supabase.ts +0 -613
  40. package/src/data/tables.ts +0 -33
  41. package/src/data/types.ts +0 -97
  42. package/src/format.ts +0 -40
  43. package/src/index.ts +0 -457
  44. package/src/integrations/ranklayer/RANKLAYER.md +0 -40
  45. package/src/integrations/ranklayer/connectorDef.tsx +0 -102
  46. package/src/integrations/ranklayer/data/supabase.ts +0 -106
  47. package/src/integrations/ranklayer/types.ts +0 -16
  48. package/src/locales/en.ts +0 -192
  49. package/src/locales/index.ts +0 -7
  50. package/src/locales/pt-BR.ts +0 -192
  51. package/src/migrations/000_plg_rename.sql +0 -15
  52. package/src/migrations/001_content_planner.sql +0 -94
  53. package/src/migrations/002_multi_platform.sql +0 -25
  54. package/src/migrations/003_recording_ops.sql +0 -25
  55. package/src/migrations/004_ranklayer.sql +0 -50
  56. package/src/migrations/005_analytics_base.sql +0 -60
  57. package/src/migrations/006_analytics_views.sql +0 -68
  58. package/src/migrations/007_agent_rpcs.sql +0 -119
  59. package/src/migrations/008_landing_pages.sql +0 -112
  60. package/src/migrations/index.ts +0 -601
  61. package/src/presets.ts +0 -119
  62. package/src/registries.ts +0 -62
  63. package/src/schema/index.ts +0 -106
  64. package/src/store.ts +0 -102
  65. package/src/types.ts +0 -153
  66. package/src/views/CampaignComposer.tsx +0 -89
  67. package/src/views/CampaignsView.tsx +0 -59
  68. package/src/views/ChannelDetailView.tsx +0 -59
  69. package/src/views/ChannelsView.tsx +0 -46
  70. package/src/views/FunnelView.tsx +0 -56
  71. package/src/views/LandingPageComposer.tsx +0 -201
  72. package/src/views/LandingPagesView.tsx +0 -172
  73. package/src/views/OverviewView.tsx +0 -24
  74. package/src/views/SettingsView.tsx +0 -72
  75. package/src/views/blog/BlogAdminView.tsx +0 -99
  76. package/src/views/blog/BlogEntityForm.tsx +0 -101
  77. package/src/views/blog/useEntityCrud.ts +0 -50
  78. package/src/views/content/ContentPlannerContext.tsx +0 -13
  79. package/src/views/content/ContentView.tsx +0 -908
  80. package/src/views/content/PlanBriefView.tsx +0 -268
  81. package/src/views/content/PostPage.tsx +0 -370
  82. package/src/views/content/PostSocialPreview.tsx +0 -212
  83. package/src/views/content/RecordingChecklist.tsx +0 -145
  84. package/src/views/content/contentBits.tsx +0 -114
  85. package/src/views/content/contentStore.ts +0 -160
  86. package/src/views/content/templates.ts +0 -172
  87. package/src/views/dashboardWidgets.tsx +0 -173
  88. /package/dist/{format.d.ts → lib/format.d.ts} +0 -0
@@ -1,102 +0,0 @@
1
- // RankLayer as a unified ConnectorDefinition, surfaced in Marketing → settings
2
- // → Integrações (rendered by the shared ConnectorsHub). SCAFFOLD: the hub gives
3
- // us the API-key form + Test/Save; the ExtraPanel below explains that automated
4
- // SEO content sync is being prepared. The real sync lands via an external PR —
5
- // see ./RANKLAYER.md.
6
- import React, { useEffect, useState } from 'react'
7
- import { Sparkles, History, CheckCircle2, AlertCircle } from 'lucide-react'
8
- import type { ConnectorDefinition } from '@fayz-ai/core'
9
- import { createRankLayerProvider } from './data/supabase'
10
- import type { RankLayerIntegration, RankLayerSyncLogEntry } from './types'
11
-
12
- const provider = createRankLayerProvider()
13
-
14
- function RankLayerExtraPanel() {
15
- const [integration, setIntegration] = useState<RankLayerIntegration | null>(null)
16
- const [log, setLog] = useState<RankLayerSyncLogEntry[]>([])
17
- const [loaded, setLoaded] = useState(false)
18
-
19
- useEffect(() => {
20
- let cancelled = false
21
- ;(async () => {
22
- try {
23
- const found = await provider.getIntegration()
24
- if (cancelled) return
25
- setIntegration(found)
26
- if (found) setLog(await provider.getSyncLog(found.id))
27
- } catch {
28
- /* not connected / no client — nothing to show */
29
- } finally {
30
- if (!cancelled) setLoaded(true)
31
- }
32
- })()
33
- return () => { cancelled = true }
34
- }, [])
35
-
36
- if (!loaded || !integration) return null // nothing until the connection is saved
37
-
38
- return (
39
- <div className="space-y-4 border-t pt-4">
40
- <div className="flex items-start gap-3 rounded-md border border-dashed p-3">
41
- <Sparkles className="mt-0.5 h-4 w-4 shrink-0 text-muted-foreground" />
42
- <div className="space-y-1">
43
- <h4 className="text-sm font-semibold">Sincronização de conteúdo — em breve</h4>
44
- <p className="text-xs text-muted-foreground">
45
- A publicação automática de páginas e artigos de SEO com a RankLayer está em preparação.
46
- {integration.siteDomain ? ` Domínio configurado: ${integration.siteDomain}.` : ''} Assim que a
47
- integração estiver ativa, o histórico de sincronizações aparece aqui.
48
- </p>
49
- </div>
50
- </div>
51
-
52
- {log.length > 0 && (
53
- <div className="space-y-2">
54
- <div className="flex items-center gap-2">
55
- <History className="h-4 w-4 text-muted-foreground" />
56
- <h4 className="text-sm font-semibold">Histórico</h4>
57
- </div>
58
- <div className="divide-y rounded-md border text-sm">
59
- {log.map((r) => (
60
- <div key={r.id} className="flex items-center gap-3 px-3 py-2">
61
- <span className="text-xs text-muted-foreground">{new Date(r.createdAt).toLocaleString('pt-BR')}</span>
62
- <span className="ml-auto text-xs">{r.written}/{r.fetched} publicadas</span>
63
- {r.status === 'success'
64
- ? <CheckCircle2 className="h-3.5 w-3.5 text-success" />
65
- : <AlertCircle className="h-3.5 w-3.5 text-warning" />}
66
- </div>
67
- ))}
68
- </div>
69
- </div>
70
- )}
71
- </div>
72
- )
73
- }
74
-
75
- // Inlined as a data URI so the mark can never fail to load from an app's domain.
76
- const RANKLAYER_MARK = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIzMiIgaGVpZ2h0PSIzMiIgdmlld0JveD0iMCAwIDMyIDMyIj48cmVjdCB3aWR0aD0iMzIiIGhlaWdodD0iMzIiIHJ4PSI3IiBmaWxsPSIjMUYyNDMxIi8+PHBvbHlnb24gcG9pbnRzPSIxNiw1LjYgMjYuNCwxMS42IDE2LDE3LjYgNS42LDExLjYiIGZpbGw9IiNGRkZGRkYiIHN0cm9rZT0iI0ZGRkZGRiIgc3Ryb2tlLXdpZHRoPSIxLjI1IiBzdHJva2UtbGluZWpvaW49InJvdW5kIi8+PGcgb3BhY2l0eT0iMC41NSI+PHBvbHlnb24gcG9pbnRzPSI1LjYsMTYuNCAxNiwyMi40IDI2LjQsMTYuNCAyNi40LDE4LjIgMTYsMjQuMiA1LjYsMTguMiIgZmlsbD0iI0ZGRkZGRiIgc3Ryb2tlPSIjRkZGRkZGIiBzdHJva2Utd2lkdGg9IjEuMjUiIHN0cm9rZS1saW5lam9pbj0icm91bmQiLz48L2c+PGcgb3BhY2l0eT0iMC4yOCI+PHBvbHlnb24gcG9pbnRzPSI1LjYsMjEuMiAxNiwyNy4yIDI2LjQsMjEuMiAyNi40LDIzIDE2LDI5IDUuNiwyMyIgZmlsbD0iI0ZGRkZGRiIgc3Ryb2tlPSIjRkZGRkZGIiBzdHJva2Utd2lkdGg9IjEuMjUiIHN0cm9rZS1saW5lam9pbj0icm91bmQiLz48L2c+PC9zdmc+'
77
-
78
- export const ranklayerConnectorDef: ConnectorDefinition = {
79
- id: 'ranklayer',
80
- hostPluginId: 'marketing',
81
- name: 'RankLayer',
82
- description: 'Publicação de conteúdo e páginas de SEO por serviço/bairro para ser encontrado no Google e em assistentes de IA.',
83
- icon: 'Search',
84
- logoUrl: RANKLAYER_MARK,
85
- authKind: 'api-key',
86
- fields: [
87
- { key: 'apiKey', label: 'API Key', type: 'password', placeholder: '••••••••' },
88
- { key: 'siteDomain', label: 'Domínio do site', type: 'text', placeholder: 'exemplo.com.br' },
89
- ],
90
- async getStatus() {
91
- try {
92
- const found = await provider.getIntegration()
93
- return { connected: !!found?.apiKey, detail: found?.siteDomain }
94
- } catch {
95
- return { connected: false }
96
- }
97
- },
98
- testConnection: (values) => provider.testConnection({ apiKey: values.apiKey }),
99
- saveConnection: async (values) => { await provider.saveIntegration({ apiKey: values.apiKey, siteDomain: values.siteDomain }) },
100
- disconnect: () => provider.disconnect(),
101
- ExtraPanel: RankLayerExtraPanel,
102
- }
@@ -1,106 +0,0 @@
1
- import { getSupabaseClientOptional, getActiveTenantId } from '@fayz-ai/core'
2
- import type { TestConnectionResult } from '@fayz-ai/core'
3
- import type { RankLayerIntegration, RankLayerSyncLogEntry } from '../types'
4
-
5
- // ---------------------------------------------------------------------------
6
- // RankLayer connector — control-plane client (SCAFFOLD).
7
- //
8
- // Reads/writes the connection row (plg_marketing_ranklayer_integrations) and
9
- // reads the sync log. `testConnection` and `sync` are stubbed: RankLayer has no
10
- // public API wired yet, so the real work (an edge function calling RankLayer)
11
- // lands via an external PR — see ../RANKLAYER.md. Everything here is safe to
12
- // ship: it only persists the tenant's API key + domain and surfaces status.
13
- // ---------------------------------------------------------------------------
14
-
15
- const INTEGRATIONS = 'plg_marketing_ranklayer_integrations'
16
- const SYNC_LOG = 'plg_marketing_ranklayer_sync_log'
17
-
18
- function getClient() {
19
- const supabase = getSupabaseClientOptional() as { from: (t: string) => any } | null
20
- if (!supabase) throw new Error('[ranklayer] Supabase not initialized')
21
- return supabase
22
- }
23
-
24
- function requireTenantId(): string {
25
- const tenantId = getActiveTenantId()
26
- if (!tenantId) throw new Error('[ranklayer] No active tenant')
27
- return tenantId
28
- }
29
-
30
- function toIntegration(row: Record<string, any>): RankLayerIntegration {
31
- return {
32
- id: row.id,
33
- apiKey: row.api_key ?? undefined,
34
- siteDomain: row.site_domain ?? undefined,
35
- active: row.active ?? true,
36
- lastSyncAt: row.last_sync_at ?? undefined,
37
- }
38
- }
39
-
40
- export function createRankLayerProvider() {
41
- return {
42
- async getIntegration(): Promise<RankLayerIntegration | null> {
43
- const tenantId = getActiveTenantId()
44
- if (!tenantId) return null
45
- const { data } = await getClient()
46
- .from(INTEGRATIONS)
47
- .select('*')
48
- .eq('tenant_id', tenantId)
49
- .maybeSingle()
50
- return data ? toIntegration(data) : null
51
- },
52
-
53
- async saveIntegration(values: { apiKey: string; siteDomain?: string }): Promise<RankLayerIntegration> {
54
- const tenantId = requireTenantId()
55
- const payload = {
56
- tenant_id: tenantId,
57
- api_key: values.apiKey,
58
- site_domain: values.siteDomain ?? null,
59
- active: true,
60
- updated_at: new Date().toISOString(),
61
- }
62
- // Upsert on the unique tenant_id.
63
- const { data, error } = await getClient()
64
- .from(INTEGRATIONS)
65
- .upsert(payload, { onConflict: 'tenant_id' })
66
- .select('*')
67
- .single()
68
- if (error) throw error
69
- return toIntegration(data)
70
- },
71
-
72
- async disconnect(): Promise<void> {
73
- const tenantId = getActiveTenantId()
74
- if (!tenantId) return
75
- await getClient().from(INTEGRATIONS).delete().eq('tenant_id', tenantId)
76
- },
77
-
78
- // SCAFFOLD: no RankLayer API yet — accept any non-empty key so the connect
79
- // flow can be exercised. Replace with a real credential check in the PR.
80
- async testConnection(values: { apiKey: string }): Promise<TestConnectionResult> {
81
- if (!values.apiKey || values.apiKey.trim().length < 8) {
82
- return { ok: false, message: 'Informe uma API Key válida da RankLayer.' }
83
- }
84
- return { ok: true, message: 'Chave aceita (validação real pendente — integração em preparação).' }
85
- },
86
-
87
- async getSyncLog(integrationId: string): Promise<RankLayerSyncLogEntry[]> {
88
- const { data } = await getClient()
89
- .from(SYNC_LOG)
90
- .select('*')
91
- .eq('integration_id', integrationId)
92
- .order('created_at', { ascending: false })
93
- .limit(20)
94
- return (data ?? []).map((r: Record<string, any>) => ({
95
- id: r.id,
96
- createdAt: r.created_at,
97
- status: r.status,
98
- message: r.message ?? undefined,
99
- fetched: r.fetched ?? 0,
100
- written: r.written ?? 0,
101
- }))
102
- },
103
- }
104
- }
105
-
106
- export type RankLayerProvider = ReturnType<typeof createRankLayerProvider>
@@ -1,16 +0,0 @@
1
- export interface RankLayerIntegration {
2
- id: string
3
- apiKey?: string
4
- siteDomain?: string
5
- active: boolean
6
- lastSyncAt?: string
7
- }
8
-
9
- export interface RankLayerSyncLogEntry {
10
- id: string
11
- createdAt: string
12
- status: 'success' | 'partial' | 'error'
13
- message?: string
14
- fetched: number
15
- written: number
16
- }
package/src/locales/en.ts DELETED
@@ -1,192 +0,0 @@
1
- export const en: Record<string, string> = {
2
- // metrics
3
- 'marketing.metric.conversionRate': 'Conversion rate',
4
- 'marketing.metric.cpa': 'Cost per acquisition',
5
- 'marketing.metric.topChannel': 'Top channel',
6
- 'marketing.metric.spend': 'spend',
7
- 'marketing.metric.reach': 'Reach',
8
- 'marketing.metric.cvr': 'CVR',
9
- 'marketing.metric.totalVisits': 'Total visits',
10
- 'marketing.metric.pages': 'Pages',
11
- 'marketing.metric.leads': 'Leads',
12
- 'marketing.metric.avgConversion': 'Avg. conversion',
13
- // columns
14
- 'marketing.col.campaign': 'Campaign',
15
- 'marketing.col.channel': 'Channel',
16
- 'marketing.col.reach': 'Reach',
17
- 'marketing.col.leads': 'Leads',
18
- 'marketing.col.cvr': 'CVR',
19
- 'marketing.col.spend': 'Spend',
20
- 'marketing.col.cpa': 'CPA',
21
- 'marketing.col.status': 'Status',
22
- 'marketing.col.visits': 'Visits',
23
- 'marketing.col.conversions': 'Conversions',
24
- 'marketing.col.type': 'Type',
25
- 'marketing.col.page': 'Page',
26
- 'marketing.col.route': 'Route',
27
- 'marketing.col.leadTag': 'Tag',
28
- // overview
29
- 'marketing.overview.byChannelSuffix': 'by channel',
30
- 'marketing.overview.recent': 'Recent campaigns',
31
- // channels
32
- 'marketing.channels.subtitle': 'Acquisition performance by channel',
33
- 'marketing.channels.campaignsOn': 'Campaigns on this channel',
34
- 'marketing.channels.noCampaigns': 'No campaigns yet on this channel.',
35
- // campaigns
36
- 'marketing.campaigns.subtitle': 'All acquisition campaigns and their conversion performance',
37
- 'marketing.campaigns.new': 'New campaign',
38
- 'marketing.campaigns.empty': 'No campaigns yet — create your first one.',
39
- 'marketing.campaigns.delete': 'Delete campaign',
40
- // composer
41
- 'marketing.composer.title': 'New campaign',
42
- 'marketing.composer.name': 'Campaign name',
43
- 'marketing.composer.namePlaceholder': 'e.g. Spring Promo — Paid Social',
44
- 'marketing.composer.channel': 'Channel',
45
- 'marketing.composer.channelPlaceholder': 'Select a channel',
46
- 'marketing.composer.status': 'Status',
47
- 'marketing.composer.budget': 'Budget / spend',
48
- 'marketing.composer.cancel': 'Cancel',
49
- 'marketing.composer.create': 'Create campaign',
50
- 'marketing.status.draft': 'draft',
51
- 'marketing.status.active': 'active',
52
- 'marketing.status.paused': 'paused',
53
- 'marketing.status.ended': 'ended',
54
- // funnel
55
- 'marketing.funnel.subtitlePrefix': 'Attribution funnel — reach to',
56
- 'marketing.funnel.overall': 'Overall conversion rate',
57
- 'marketing.funnel.ofPrev': 'of prev',
58
- // landing pages
59
- 'marketing.landing.new': 'New page',
60
- 'marketing.landing.edit': 'Edit page',
61
- 'marketing.landing.delete': 'Delete page',
62
- 'marketing.landing.save': 'Save',
63
- 'marketing.landing.empty': 'No pages yet.',
64
- 'marketing.landing.site': 'Site',
65
- 'marketing.landing.sitePlaceholder': 'Pick a site',
66
- 'marketing.landing.name': 'Name',
67
- 'marketing.landing.namePlaceholder': 'Hair consult LP',
68
- 'marketing.landing.route': 'Route',
69
- 'marketing.landing.create': 'Create page',
70
- 'marketing.landing.status': 'Does this page exist on the site yet?',
71
- 'marketing.landing.statusLive': 'Already live',
72
- 'marketing.landing.statusPlanned': 'Not yet',
73
- 'marketing.landing.prompt': 'Page prompt',
74
- 'marketing.landing.promptPlaceholder': 'Describe the page: offer, audience, sections, call to action…',
75
- 'marketing.landing.buildWithFayz': 'Build page with Fayz',
76
- 'marketing.landing.leadTag': 'Lead tags',
77
- 'marketing.landing.leadTagPlaceholder': 'new tag…',
78
- 'marketing.landing.leadTagHint': 'Leads from this page enter the CRM with these tags.',
79
- 'marketing.landing.visitsPending': 'Visits come from the platform hosting the site; that source is not connected yet.',
80
- // settings
81
- 'marketing.settings.conversionModel': 'Conversion model',
82
- 'marketing.settings.conversionModelDesc': "What counts as a conversion in this workspace and where it's attributed from.",
83
- 'marketing.settings.conversion': 'Conversion',
84
- 'marketing.settings.valueMetric': 'Value metric',
85
- 'marketing.settings.attributedFrom': 'Attributed from',
86
- 'marketing.settings.channels': 'Acquisition channels',
87
- 'marketing.settings.channelsDesc': 'Sources tracked for this workspace.',
88
- 'marketing.settings.trackValue': 'Track monetary value',
89
- 'marketing.settings.trackValueDesc': 'Attribute revenue to channels and campaigns.',
90
- 'marketing.settings.attribution': 'Attribution',
91
- 'marketing.settings.attributionDesc': 'How conversions are matched to channels.',
92
- 'marketing.settings.autoSync': 'Auto-sync conversions',
93
- 'marketing.settings.autoSyncDesc': 'Pull conversions automatically from the source.',
94
- 'marketing.settings.assisted': 'Count assisted conversions',
95
- 'marketing.settings.assistedDesc': 'Credit channels that contributed to a conversion.',
96
- 'marketing.settings.channelTrackDesc': 'Track this acquisition source.',
97
- // content planner
98
- 'marketing.content.title': 'Content',
99
- 'marketing.content.week': 'Week',
100
- 'marketing.content.month': 'Month',
101
- 'marketing.content.newPost': 'New post',
102
- 'marketing.content.untitled': 'Untitled',
103
- 'marketing.content.account': 'Account',
104
- 'marketing.content.addAccount': 'Add account',
105
- 'marketing.content.previewNow': 'Just now',
106
- 'marketing.content.previewLike': 'Like',
107
- 'marketing.content.previewComment': 'Comment',
108
- 'marketing.content.previewShare': 'Share',
109
- 'marketing.content.showLess': 'show less',
110
- 'marketing.content.checklistTitle': 'Recording checklist',
111
- 'marketing.content.checklistDescription': 'Shooting-day operations',
112
- 'marketing.content.checklistOpen': 'Checklist',
113
- 'marketing.content.checklistEmpty': 'No items yet — generate them from the script or add your own below.',
114
- 'marketing.content.checklistAddPlaceholder': 'New item…',
115
- 'marketing.content.checklistAdd': 'Add item',
116
- 'marketing.content.checklistGenerate': 'Generate from script',
117
- 'marketing.content.scriptLabel': 'Script',
118
- 'marketing.content.captionLabel': 'Caption',
119
- 'marketing.content.captionPlaceholder': 'Write the post caption…',
120
- 'marketing.content.mediaLabel': 'Media asset',
121
- 'marketing.content.mediaUpload': 'Upload media',
122
- 'marketing.content.mediaHint': 'Image or video for this post',
123
- 'marketing.content.mediaReplace': 'Replace',
124
- 'marketing.content.mediaRemove': 'Remove',
125
- 'marketing.content.mediaUnavailable': 'Media upload requires a connected Supabase project.',
126
- 'marketing.content.editAccountTitle': 'Account settings',
127
- 'marketing.content.accountSettings': 'Account settings',
128
- 'marketing.content.deleteAccount': 'Delete account',
129
- 'marketing.content.deleteAccountConfirm': 'The account and all of its content plans and posts will be permanently deleted.',
130
- 'marketing.content.noAccountsAvailable': 'No accounts available',
131
- 'marketing.content.addAccountTitle': 'New account',
132
- 'marketing.content.nameLabel': 'Name',
133
- 'marketing.content.handleLabel': 'Handle',
134
- 'marketing.content.handlePlaceholder': '@handle (optional)',
135
- 'marketing.content.viewBoard': 'Weekly',
136
- 'marketing.content.viewCalendar': 'Monthly',
137
- 'marketing.content.viewList': 'List',
138
- 'marketing.content.unscheduled': 'Unscheduled',
139
- 'marketing.content.noPosts': 'No posts yet — add your first one from any week.',
140
- 'marketing.content.listTitle': 'Title',
141
- 'marketing.content.listWeek': 'Week',
142
- 'marketing.content.listDate': 'Date',
143
- 'marketing.content.listFormat': 'Format',
144
- 'marketing.content.listStatus': 'Status',
145
- 'marketing.content.noAccountsHint': 'Create the account (e.g. your Instagram profile) to start planning content.',
146
- 'marketing.content.accountNamePlaceholder': 'e.g. @myclinic',
147
- 'marketing.content.createAccount': 'Create account',
148
- 'marketing.content.defaultPlanName': 'Content plan — 8 weeks',
149
- 'marketing.content.brief': 'Brief',
150
- 'marketing.content.planName': 'Plan name',
151
- 'marketing.content.objective': 'Objective',
152
- 'marketing.content.objectivePlaceholder': 'e.g. generate evaluation bookings',
153
- 'marketing.content.tone': 'Tone of voice',
154
- 'marketing.content.tonePlaceholder': 'e.g. approachable authority, plain language',
155
- 'marketing.content.pillars': 'Content pillars',
156
- 'marketing.content.pillarPlaceholder': 'Add pillar…',
157
- 'marketing.content.formatsLabel': 'Formats',
158
- 'marketing.content.weeklyFrequency': 'Posts per week',
159
- 'marketing.content.weeksCount': 'Weeks',
160
- 'marketing.content.briefBody': 'Brief (markdown)',
161
- 'marketing.content.briefPlaceholder': 'Describe the strategy, references and what the content should achieve…',
162
- 'marketing.content.formatLabel': 'Format',
163
- 'marketing.content.statusLabel': 'Status',
164
- 'marketing.content.scheduledDate': 'Planned date',
165
- 'marketing.content.hook': 'Hook',
166
- 'marketing.content.hookPlaceholder': 'The first line that grabs attention…',
167
- 'marketing.content.cta': 'CTA',
168
- 'marketing.content.ctaPlaceholder': 'e.g. Book your evaluation',
169
- 'marketing.content.titlePlaceholder': 'Post title…',
170
- 'marketing.content.bodyPlaceholder': 'Post script / plan in markdown…',
171
- 'marketing.content.insertTemplate': 'Insert script template',
172
- 'marketing.content.save': 'Save',
173
- 'marketing.content.cancel': 'Cancel',
174
- 'marketing.content.edit': 'Edit',
175
- 'marketing.content.preview': 'Preview',
176
- 'marketing.content.deletePost': 'Delete post',
177
- 'marketing.content.deletePostConfirm': 'This post and its script will be permanently removed.',
178
- 'marketing.content.format.reel': 'Reel',
179
- 'marketing.content.format.static': 'Static',
180
- 'marketing.content.format.carousel': 'Carousel',
181
- 'marketing.content.format.story': 'Story',
182
- 'marketing.content.format.video': 'Video',
183
- 'marketing.content.format.live': 'Live',
184
- 'marketing.content.format.article': 'Article',
185
- 'marketing.content.platformsLabel': 'Platforms',
186
- 'marketing.content.status.idea': 'idea',
187
- 'marketing.content.status.script': 'script',
188
- 'marketing.content.status.recording': 'recording',
189
- 'marketing.content.status.editing': 'editing',
190
- 'marketing.content.status.scheduled': 'scheduled',
191
- 'marketing.content.status.published': 'published',
192
- }
@@ -1,7 +0,0 @@
1
- import { en } from './en'
2
- import { ptBR } from './pt-BR'
3
-
4
- export const marketingLocales: Record<string, Record<string, string>> = {
5
- en,
6
- 'pt-BR': ptBR,
7
- }
@@ -1,192 +0,0 @@
1
- export const ptBR: Record<string, string> = {
2
- // metrics
3
- 'marketing.metric.conversionRate': 'Taxa de conversão',
4
- 'marketing.metric.cpa': 'Custo por aquisição',
5
- 'marketing.metric.topChannel': 'Principal canal',
6
- 'marketing.metric.spend': 'investido',
7
- 'marketing.metric.reach': 'Alcance',
8
- 'marketing.metric.cvr': 'CVR',
9
- 'marketing.metric.totalVisits': 'Total de visitas',
10
- 'marketing.metric.pages': 'Páginas',
11
- 'marketing.metric.leads': 'Leads',
12
- 'marketing.metric.avgConversion': 'Conversão média',
13
- // columns
14
- 'marketing.col.campaign': 'Campanha',
15
- 'marketing.col.channel': 'Canal',
16
- 'marketing.col.reach': 'Alcance',
17
- 'marketing.col.leads': 'Leads',
18
- 'marketing.col.cvr': 'CVR',
19
- 'marketing.col.spend': 'Investimento',
20
- 'marketing.col.cpa': 'CPA',
21
- 'marketing.col.status': 'Status',
22
- 'marketing.col.visits': 'Visitas',
23
- 'marketing.col.conversions': 'Conversões',
24
- 'marketing.col.type': 'Tipo',
25
- 'marketing.col.page': 'Página',
26
- 'marketing.col.route': 'Rota',
27
- 'marketing.col.leadTag': 'Tag',
28
- // overview
29
- 'marketing.overview.byChannelSuffix': 'por canal',
30
- 'marketing.overview.recent': 'Campanhas recentes',
31
- // channels
32
- 'marketing.channels.subtitle': 'Desempenho de aquisição por canal',
33
- 'marketing.channels.campaignsOn': 'Campanhas neste canal',
34
- 'marketing.channels.noCampaigns': 'Nenhuma campanha ainda neste canal.',
35
- // campaigns
36
- 'marketing.campaigns.subtitle': 'Todas as campanhas de aquisição e seu desempenho de conversão',
37
- 'marketing.campaigns.new': 'Nova campanha',
38
- 'marketing.campaigns.empty': 'Nenhuma campanha ainda — crie a primeira.',
39
- 'marketing.campaigns.delete': 'Excluir campanha',
40
- // composer
41
- 'marketing.composer.title': 'Nova campanha',
42
- 'marketing.composer.name': 'Nome da campanha',
43
- 'marketing.composer.namePlaceholder': 'ex.: Promo de Verão — Mídia Paga',
44
- 'marketing.composer.channel': 'Canal',
45
- 'marketing.composer.channelPlaceholder': 'Selecione um canal',
46
- 'marketing.composer.status': 'Status',
47
- 'marketing.composer.budget': 'Orçamento / investimento',
48
- 'marketing.composer.cancel': 'Cancelar',
49
- 'marketing.composer.create': 'Criar campanha',
50
- 'marketing.status.draft': 'rascunho',
51
- 'marketing.status.active': 'ativa',
52
- 'marketing.status.paused': 'pausada',
53
- 'marketing.status.ended': 'encerrada',
54
- // funnel
55
- 'marketing.funnel.subtitlePrefix': 'Funil de atribuição — alcance até',
56
- 'marketing.funnel.overall': 'Taxa de conversão geral',
57
- 'marketing.funnel.ofPrev': 'do anterior',
58
- // landing pages
59
- 'marketing.landing.new': 'Nova página',
60
- 'marketing.landing.edit': 'Editar página',
61
- 'marketing.landing.delete': 'Excluir página',
62
- 'marketing.landing.save': 'Salvar',
63
- 'marketing.landing.empty': 'Nenhuma página cadastrada ainda.',
64
- 'marketing.landing.site': 'Site',
65
- 'marketing.landing.sitePlaceholder': 'Selecione o site',
66
- 'marketing.landing.name': 'Nome',
67
- 'marketing.landing.namePlaceholder': 'LP Capilar',
68
- 'marketing.landing.route': 'Rota',
69
- 'marketing.landing.create': 'Criar página',
70
- 'marketing.landing.status': 'Esta página já existe no site?',
71
- 'marketing.landing.statusLive': 'Já existe',
72
- 'marketing.landing.statusPlanned': 'Ainda não existe',
73
- 'marketing.landing.prompt': 'Prompt da página',
74
- 'marketing.landing.promptPlaceholder': 'Descreva a página: oferta, público, seções, chamada para ação…',
75
- 'marketing.landing.buildWithFayz': 'Criar página com Fayz',
76
- 'marketing.landing.leadTag': 'Tags dos leads',
77
- 'marketing.landing.leadTagPlaceholder': 'nova tag…',
78
- 'marketing.landing.leadTagHint': 'Os leads desta página entram no CRM com estas tags.',
79
- 'marketing.landing.visitsPending': 'As visitas vêm da plataforma que hospeda o site; essa origem ainda não está conectada.',
80
- // settings
81
- 'marketing.settings.conversionModel': 'Modelo de conversão',
82
- 'marketing.settings.conversionModelDesc': 'O que conta como conversão neste workspace e de onde é atribuída.',
83
- 'marketing.settings.conversion': 'Conversão',
84
- 'marketing.settings.valueMetric': 'Métrica de valor',
85
- 'marketing.settings.attributedFrom': 'Atribuída de',
86
- 'marketing.settings.channels': 'Canais de aquisição',
87
- 'marketing.settings.channelsDesc': 'Fontes monitoradas neste workspace.',
88
- 'marketing.settings.trackValue': 'Acompanhar valor monetário',
89
- 'marketing.settings.trackValueDesc': 'Atribuir receita a canais e campanhas.',
90
- 'marketing.settings.attribution': 'Atribuição',
91
- 'marketing.settings.attributionDesc': 'Como as conversões são associadas aos canais.',
92
- 'marketing.settings.autoSync': 'Sincronizar conversões automaticamente',
93
- 'marketing.settings.autoSyncDesc': 'Importar conversões automaticamente da fonte.',
94
- 'marketing.settings.assisted': 'Contar conversões assistidas',
95
- 'marketing.settings.assistedDesc': 'Creditar canais que contribuíram para uma conversão.',
96
- 'marketing.settings.channelTrackDesc': 'Monitorar esta fonte de aquisição.',
97
- // content planner
98
- 'marketing.content.title': 'Conteúdo',
99
- 'marketing.content.week': 'Semana',
100
- 'marketing.content.month': 'Mês',
101
- 'marketing.content.newPost': 'Novo post',
102
- 'marketing.content.untitled': 'Sem título',
103
- 'marketing.content.account': 'Conta',
104
- 'marketing.content.addAccount': 'Adicionar conta',
105
- 'marketing.content.previewNow': 'Agora',
106
- 'marketing.content.previewLike': 'Curtir',
107
- 'marketing.content.previewComment': 'Comentar',
108
- 'marketing.content.previewShare': 'Compartilhar',
109
- 'marketing.content.showLess': 'mostrar menos',
110
- 'marketing.content.checklistTitle': 'Checklist de gravação',
111
- 'marketing.content.checklistDescription': 'Operação do dia de gravação',
112
- 'marketing.content.checklistOpen': 'Checklist',
113
- 'marketing.content.checklistEmpty': 'Nenhum item ainda — gere a partir do roteiro ou adicione o seu abaixo.',
114
- 'marketing.content.checklistAddPlaceholder': 'Novo item…',
115
- 'marketing.content.checklistAdd': 'Adicionar item',
116
- 'marketing.content.checklistGenerate': 'Gerar do roteiro',
117
- 'marketing.content.scriptLabel': 'Roteiro',
118
- 'marketing.content.captionLabel': 'Legenda',
119
- 'marketing.content.captionPlaceholder': 'Escreva a legenda do post…',
120
- 'marketing.content.mediaLabel': 'Arte / mídia',
121
- 'marketing.content.mediaUpload': 'Enviar mídia',
122
- 'marketing.content.mediaHint': 'Imagem ou vídeo deste post',
123
- 'marketing.content.mediaReplace': 'Substituir',
124
- 'marketing.content.mediaRemove': 'Remover',
125
- 'marketing.content.mediaUnavailable': 'O upload de mídia requer um projeto Supabase conectado.',
126
- 'marketing.content.editAccountTitle': 'Configurações da conta',
127
- 'marketing.content.accountSettings': 'Configurações da conta',
128
- 'marketing.content.deleteAccount': 'Excluir conta',
129
- 'marketing.content.deleteAccountConfirm': 'A conta e todos os planos e posts de conteúdo dela serão excluídos permanentemente.',
130
- 'marketing.content.noAccountsAvailable': 'Nenhuma conta disponível',
131
- 'marketing.content.addAccountTitle': 'Nova conta',
132
- 'marketing.content.nameLabel': 'Nome',
133
- 'marketing.content.handleLabel': 'Usuário',
134
- 'marketing.content.handlePlaceholder': '@usuario (opcional)',
135
- 'marketing.content.viewBoard': 'Semanal',
136
- 'marketing.content.viewCalendar': 'Mensal',
137
- 'marketing.content.viewList': 'Lista',
138
- 'marketing.content.unscheduled': 'Sem data',
139
- 'marketing.content.noPosts': 'Nenhum post ainda — adicione o primeiro em qualquer semana.',
140
- 'marketing.content.listTitle': 'Título',
141
- 'marketing.content.listWeek': 'Semana',
142
- 'marketing.content.listDate': 'Data',
143
- 'marketing.content.listFormat': 'Formato',
144
- 'marketing.content.listStatus': 'Status',
145
- 'marketing.content.noAccountsHint': 'Crie a conta (ex.: o perfil do Instagram) para começar a planejar o conteúdo.',
146
- 'marketing.content.accountNamePlaceholder': 'ex.: @minhaclinica',
147
- 'marketing.content.createAccount': 'Criar conta',
148
- 'marketing.content.defaultPlanName': 'Plano de conteúdo — 8 semanas',
149
- 'marketing.content.brief': 'Briefing',
150
- 'marketing.content.planName': 'Nome do plano',
151
- 'marketing.content.objective': 'Objetivo',
152
- 'marketing.content.objectivePlaceholder': 'ex.: gerar agendamentos de avaliação',
153
- 'marketing.content.tone': 'Tom de voz',
154
- 'marketing.content.tonePlaceholder': 'ex.: autoridade acessível, linguagem simples',
155
- 'marketing.content.pillars': 'Pilares de conteúdo',
156
- 'marketing.content.pillarPlaceholder': 'Adicionar pilar…',
157
- 'marketing.content.formatsLabel': 'Formatos',
158
- 'marketing.content.weeklyFrequency': 'Posts por semana',
159
- 'marketing.content.weeksCount': 'Semanas',
160
- 'marketing.content.briefBody': 'Briefing (markdown)',
161
- 'marketing.content.briefPlaceholder': 'Descreva a estratégia, referências e o que se quer com o conteúdo…',
162
- 'marketing.content.formatLabel': 'Formato',
163
- 'marketing.content.statusLabel': 'Status',
164
- 'marketing.content.scheduledDate': 'Data prevista',
165
- 'marketing.content.hook': 'Gancho',
166
- 'marketing.content.hookPlaceholder': 'A primeira frase que segura a atenção…',
167
- 'marketing.content.cta': 'CTA',
168
- 'marketing.content.ctaPlaceholder': 'ex.: Agende sua avaliação',
169
- 'marketing.content.titlePlaceholder': 'Título do post…',
170
- 'marketing.content.bodyPlaceholder': 'Roteiro / plano do post em markdown…',
171
- 'marketing.content.insertTemplate': 'Inserir modelo de roteiro',
172
- 'marketing.content.save': 'Salvar',
173
- 'marketing.content.cancel': 'Cancelar',
174
- 'marketing.content.edit': 'Editar',
175
- 'marketing.content.preview': 'Visualizar',
176
- 'marketing.content.deletePost': 'Excluir post',
177
- 'marketing.content.deletePostConfirm': 'Este post e seu roteiro serão removidos permanentemente.',
178
- 'marketing.content.format.reel': 'Reel',
179
- 'marketing.content.format.static': 'Estático',
180
- 'marketing.content.format.carousel': 'Carrossel',
181
- 'marketing.content.format.story': 'Story',
182
- 'marketing.content.format.video': 'Vídeo',
183
- 'marketing.content.format.live': 'Live',
184
- 'marketing.content.format.article': 'Artigo',
185
- 'marketing.content.platformsLabel': 'Plataformas',
186
- 'marketing.content.status.idea': 'ideia',
187
- 'marketing.content.status.script': 'roteiro',
188
- 'marketing.content.status.recording': 'gravação',
189
- 'marketing.content.status.editing': 'edição',
190
- 'marketing.content.status.scheduled': 'agendado',
191
- 'marketing.content.status.published': 'publicado',
192
- }
@@ -1,15 +0,0 @@
1
- -- 000_plg_rename.sql — rename legacy marketing tables to plg_marketing_* for pools
2
- -- provisioned before the industry-pool rename. Guarded: fires only when legacy
3
- -- name exists and target does not, so fresh pools skip every branch.
4
- DO $$
5
- BEGIN
6
- IF to_regclass('public.mkt_social_accounts') IS NOT NULL AND to_regclass('public.plg_marketing_social_accounts') IS NULL THEN
7
- ALTER TABLE public.mkt_social_accounts RENAME TO plg_marketing_social_accounts;
8
- END IF;
9
- IF to_regclass('public.mkt_content_plans') IS NOT NULL AND to_regclass('public.plg_marketing_content_plans') IS NULL THEN
10
- ALTER TABLE public.mkt_content_plans RENAME TO plg_marketing_content_plans;
11
- END IF;
12
- IF to_regclass('public.mkt_content_posts') IS NOT NULL AND to_regclass('public.plg_marketing_content_posts') IS NULL THEN
13
- ALTER TABLE public.mkt_content_posts RENAME TO plg_marketing_content_posts;
14
- END IF;
15
- END $$;