@fayz-ai/db 0.14.0 → 0.16.0
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.
- package/migrations/011_leadcontrol_joins_the_family.sql +5 -1
- package/migrations/029_o_administrador_enxerga_o_que_existe.sql +167 -0
- package/migrations/031_ticketcontrol_joins_the_family.sql +109 -0
- package/migrations/032_a_marca_e_um_conjunto_de_tokens.sql +291 -0
- package/migrations/041_a_entrega_tem_onde_guardar_a_politica.sql +53 -0
- package/migrations/042_o_token_de_marca_fecha_a_porta_anon.sql +17 -0
- package/migrations/043_fiscalcontrol_joins_the_family.sql +75 -0
- package/migrations/044_o_teste_gratis_dura_trinta_dias.sql +15 -0
- package/migrations/045_o_espelho_manda_enquanto_o_v2_nao_escreveu.sql +519 -0
- package/migrations/046_the_tick_advances_the_flows.sql +77 -0
- package/migrations/047_the_tick_sweeps_the_offers.sql +74 -0
- package/migrations/052_fullcontrol_joins_the_family.sql +213 -0
- package/migrations/053_o_erp_para_de_exigir_o_que_nao_desenha.sql +37 -0
- package/migrations/054_o_arquivo_migrado_ganha_bytes.sql +439 -0
- package/migrations/061_a_linha_sem_data_pode_ser_estrutura.sql +253 -0
- package/migrations/063_o_anexo_ja_registrado_recebe_os_bytes.sql +303 -0
- package/migrations/064_um_arquivo_anexado_a_dezenove_contas.sql +178 -0
- package/migrations/065_apagar_o_sobrevivente_nao_apaga_o_tenant.sql +59 -0
- package/migrations/066_the_port_machinery_closes_its_doors.sql +51 -0
- package/migrations/067_the_erp_keeps_the_front_door.sql +56 -0
- package/migrations/068_o_ledger_encontra_a_linha_pelo_caminho_que_o_writer_usa.sql +55 -0
- package/migrations/069_a_sondagem_de_irma_usa_indice.sql +293 -0
- package/migrations/071_o_sdr_trabalha_o_funil_e_mais_nada.sql +110 -0
- package/migrations/072_o_porte_nao_avisa_a_plataforma_de_uma_venda_antiga.sql +91 -0
- package/migrations/073_a_exclusao_e_contrato_do_cliente_nao_do_cluster.sql +300 -0
- package/migrations/074_a_classificacao_quebrada_nao_leva_o_dinheiro_junto.sql +332 -0
- package/migrations/075_o_apelido_da_referencia_nao_disputa_com_a_variavel.sql +276 -0
- package/migrations/076_o_desligado_tambem_precisa_atravessar.sql +110 -0
- package/migrations/077_a_unidade_escolhida_chega_ao_banco.sql +128 -0
- package/migrations/078_onde_o_profissional_atende_atravessa.sql +71 -0
- package/migrations/079_quem_aparece_na_agenda_e_quem_atende.sql +210 -0
- package/migrations/080_o_contexto_da_requisicao_se_calcula_uma_vez.sql +240 -0
- package/migrations/081_a_permissao_por_unidade_se_resolve_uma_vez_por_unidade.sql +88 -0
- package/migrations/082_a_cerca_tambem_se_lembra.sql +110 -0
- package/migrations/083_quem_e_profissional_continua_agendavel.sql +32 -0
- package/migrations/084_quem_atende_em_varias_nao_cabe_numa_coluna.sql +60 -0
- package/migrations/085_a_unidade_do_profissional_vem_de_onde_ele_atende.sql +47 -0
- package/migrations/086_a_ponte_de_clientes_volta_a_existir.sql +89 -0
- package/migrations/087_a_liberacao_pergunta_ao_indice_antes_da_funcao.sql +79 -0
- package/migrations/088_quem_pode_executar_o_que_o_porte_criou.sql +50 -0
- package/migrations/089_a_memoria_de_transacao_nao_serve_para_isto.sql +86 -0
- package/migrations/090_a_marca_tambem_conta_como_mudanca_de_configuracao.sql +75 -0
- package/migrations/091_o_shell_tambem_e_configuracao_do_tenant.sql +71 -0
- package/migrations/092_a_marca_tem_uma_grafia_so_e_uma_porta_so.sql +159 -0
- package/migrations/093_a_marca_carrega_o_tema_inteiro.sql +179 -0
- package/migrations/094_a_porta_do_dominio_aprende_a_grafia_nova.sql +55 -0
- package/migrations/095_o_aplicativo_tem_um_preset_que_a_conta_estende.sql +98 -0
- package/migrations/096_o_sublinhado_tambem_e_nome_de_modulo.sql +100 -0
- package/migrations/097_o_oficio_diz_o_que_oferece_e_a_conta_o_que_usa.sql +137 -0
- package/migrations/098_a_arvore_do_oficio_nomeia_quem_ainda_desenha.sql +22 -0
- package/migrations/099_o_estoque_mora_dentro_de_produtos.sql +48 -0
- package/migrations/100_o_chefcontrol_desenha_abas_de_modulo.sql +24 -0
- package/migrations/101_o_oficio_agrupa_o_plugin_nomeia.sql +93 -0
- package/migrations/102_tres_marcas_ganham_fundo_e_contraste.sql +73 -0
- package/migrations/103_great_djs_e_iam_club_entram_na_frota.sql +75 -0
- package/migrations/104_o_curso_e_a_bilheteria_ganham_preset.sql +29 -0
- package/migrations/105_o_que_o_oficio_exige_ele_tambem_oferece.sql +78 -0
- package/migrations/106_a_comunidade_se_gere_como_escola.sql +84 -0
- package/migrations/107_o_logo_da_coluna_e_projecao_do_documento.sql +53 -0
- package/migrations/108_o_modulo_do_oficio_vizinho_tem_onde_ser_visto.sql +78 -0
- package/migrations/109_a_conta_diz_o_que_nao_usa_e_a_marca_pinta_a_casa.sql +67 -0
- package/migrations/110_a_tabela_de_ramos_aprende_o_vocabulario_fechado.sql +60 -0
- package/migrations/111_o_erp_generico_nao_vende_por_pedido.sql +50 -0
- package/migrations/112_o_cache_do_contexto_sabe_de_quem_ele_e.sql +327 -0
- package/migrations/113_o_ramo_volta_a_saber_que_papeis_a_conta_nasce_tendo.sql +124 -0
- package/migrations/114_a_resposta_inicial_volta_a_dizer_o_que_a_pessoa_pode.sql +93 -0
- package/migrations/115_o_rail_e_de_quem_tem_um.sql +108 -0
- package/package.json +1 -1
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
-- ---------------------------------------------------------------------------
|
|
2
|
+
-- 088_a_comunidade_se_gere_como_escola.sql
|
|
3
|
+
-- ---------------------------------------------------------------------------
|
|
4
|
+
-- Duas coisas, e a segunda é consequência da primeira.
|
|
5
|
+
--
|
|
6
|
+
-- ── `hide`: o contrário de `under` ─────────────────────────────────────────
|
|
7
|
+
-- Com o preset agrupando em vez de redesenhar (083), quem não fosse nomeado ia
|
|
8
|
+
-- para o FIM em vez de sumir — regra que existe para um plugin novo não
|
|
9
|
+
-- desaparecer em silêncio. Só que no ChefControl isso devolveu cinco linhas
|
|
10
|
+
-- soltas embaixo de sete rubricas curadas: Agenda, CRM, Marketing, Automações,
|
|
11
|
+
-- Comunicação.
|
|
12
|
+
--
|
|
13
|
+
-- Não era ruído: no vocabulário do ChefControl a Agenda É "Reservas" e o CRM É
|
|
14
|
+
-- "Clientes". Mostrar os dois nomes é mostrar o mesmo módulo duas vezes. O
|
|
15
|
+
-- resto-saas resolve isso com `hideNav()` em código; faltava a forma em dado.
|
|
16
|
+
--
|
|
17
|
+
-- `hide` são prefixos de rota que este ofício NÃO põe no rail. Aplicado ANTES
|
|
18
|
+
-- das rubricas: uma rota calada não pode ser reivindicada nem sobrar no fim.
|
|
19
|
+
--
|
|
20
|
+
-- ── A escola passa a servir COMUNIDADE ─────────────────────────────────────
|
|
21
|
+
-- IAM Club estava na bilheteria e as cinco contas dela — IAM, Great DJs, Crema
|
|
22
|
+
-- Club, Airfryer Collective, Aldeia Zen — são a mesma forma: programa,
|
|
23
|
+
-- encontro, membro, ingresso. Um clube se gere como comunidade, não como
|
|
24
|
+
-- portaria de evento avulso.
|
|
25
|
+
--
|
|
26
|
+
-- `orders` entra na oferta porque O INGRESSO É UM PEDIDO. Não há plugin de
|
|
27
|
+
-- `ticketing`, nem de `events`, nem de `community` — o que existe é `agenda`
|
|
28
|
+
-- (o encontro), `orders` (o ingresso), `conversations` + `crm` (a comunidade).
|
|
29
|
+
-- As rubricas usam as palavras do ramo; os módulos são os que existem.
|
|
30
|
+
--
|
|
31
|
+
-- O que isto NÃO decide: se "CommunityControl" merece ser um aplicativo
|
|
32
|
+
-- próprio. Hoje cinco contas de comunidade rodam a escola porque a forma serve;
|
|
33
|
+
-- o dia em que o vocabulário "aluno/turma" atrapalhar mais do que ajudar é o
|
|
34
|
+
-- dia de separar, e aí é uma linha em `app.apps`.
|
|
35
|
+
-- ---------------------------------------------------------------------------
|
|
36
|
+
|
|
37
|
+
UPDATE app.apps SET preset = preset
|
|
38
|
+
|| '{"hide":["/agenda","/sales","/marketing","/automations","/communication"]}'::jsonb
|
|
39
|
+
WHERE id = 'resto';
|
|
40
|
+
|
|
41
|
+
UPDATE app.apps SET preset = preset || $$
|
|
42
|
+
{
|
|
43
|
+
"modules": ["dashboard","admin","notifications","conversations","courses","agenda","orders",
|
|
44
|
+
"crm","financial","marketing","automations","reports","custom_forms"],
|
|
45
|
+
"rail": ["dashboard", "conversations",
|
|
46
|
+
{ "label": "Programa", "icon": "GraduationCap", "under": ["/courses"] },
|
|
47
|
+
{ "label": "Encontros", "icon": "CalendarDays", "under": ["/agenda", "/orders"] },
|
|
48
|
+
"crm", "marketing", "financial", "reports"],
|
|
49
|
+
"hide": ["/automations", "/communication"]
|
|
50
|
+
}
|
|
51
|
+
$$::jsonb WHERE id = 'school';
|
|
52
|
+
|
|
53
|
+
-- IAM Club sai da bilheteria.
|
|
54
|
+
UPDATE app.tenant_apps SET app_id = 'school', updated_at = now()
|
|
55
|
+
WHERE tenant_id = (SELECT id FROM public.tenants WHERE slug = 'iam-club') AND app_id = 'ticket';
|
|
56
|
+
UPDATE public.tenants SET vertical_id = 'education', updated_at = now() WHERE slug = 'iam-club';
|
|
57
|
+
DELETE FROM app.tenant_plugins
|
|
58
|
+
WHERE tenant_id = (SELECT id FROM public.tenants WHERE slug = 'iam-club')
|
|
59
|
+
AND plugin_id IN ('menu','inventory');
|
|
60
|
+
|
|
61
|
+
-- O que uma comunidade liga.
|
|
62
|
+
INSERT INTO app.tenant_plugins (tenant_id, plugin_id, facet, status, source)
|
|
63
|
+
SELECT t.id, p.plugin_id, '', 'active', 'manual'
|
|
64
|
+
FROM public.tenants t
|
|
65
|
+
CROSS JOIN (VALUES ('courses'),('agenda'),('orders'),('crm'),('conversations'),
|
|
66
|
+
('marketing'),('financial'),('reports')) AS p(plugin_id)
|
|
67
|
+
WHERE t.slug IN ('iam-club','crema-club','airfryer-collective','aldeia-zen','great-djs')
|
|
68
|
+
ON CONFLICT (tenant_id, plugin_id, facet) DO UPDATE SET status = 'active', updated_at = now();
|
|
69
|
+
|
|
70
|
+
-- A invariante da 087, de novo: oferecer mais do que se exige é o normal; o
|
|
71
|
+
-- contrário nunca é.
|
|
72
|
+
DO $$
|
|
73
|
+
DECLARE v_bad text;
|
|
74
|
+
BEGIN
|
|
75
|
+
SELECT string_agg(a.id || ' exige ' || r.m || ' e não oferece', '; ') INTO v_bad
|
|
76
|
+
FROM app.apps a CROSS JOIN LATERAL unnest(a.requires) AS r(m)
|
|
77
|
+
WHERE a.active
|
|
78
|
+
-- Ver a nota na 101: a verificação nomeia a divergência, não derruba a
|
|
79
|
+
-- cadeia por causa de uma forma que ela não esperava.
|
|
80
|
+
AND jsonb_typeof(a.preset->'modules') = 'array'
|
|
81
|
+
AND jsonb_array_length(a.preset->'modules') > 0
|
|
82
|
+
AND NOT (a.preset->'modules' ? r.m);
|
|
83
|
+
IF v_bad IS NOT NULL THEN RAISE EXCEPTION 'requires fora da oferta: %', v_bad; END IF;
|
|
84
|
+
END $$;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
-- ---------------------------------------------------------------------------
|
|
2
|
+
-- 089_o_logo_da_coluna_e_projecao_do_documento.sql
|
|
3
|
+
-- ---------------------------------------------------------------------------
|
|
4
|
+
-- Great DJs e IAM Club tinham marca e apareciam SEM marca no seletor de contas.
|
|
5
|
+
-- O motivo é velho conhecido nesta base: duas grafias do mesmo fato.
|
|
6
|
+
--
|
|
7
|
+
-- o seletor lê v_team_members → tenants.logo_url (coluna, de 2024)
|
|
8
|
+
-- a marca mora em settings.branding.identity.logoUrl (documento, 075)
|
|
9
|
+
--
|
|
10
|
+
-- Quem escreve a marca pela porta nova não toca na coluna velha, e a lista
|
|
11
|
+
-- mostra iniciais. Sincronizar à mão criaria a terceira oportunidade de
|
|
12
|
+
-- divergir — é o mesmo erro que a 074 já corrigiu uma vez para `primary_color`.
|
|
13
|
+
--
|
|
14
|
+
-- A coluna vira DERIVADA: um escritor (o documento), um gatilho, e todo leitor
|
|
15
|
+
-- antigo segue funcionando sem saber que mudou de fonte.
|
|
16
|
+
--
|
|
17
|
+
-- `update OF settings` e não `update`: o gatilho existe para acompanhar a
|
|
18
|
+
-- marca, e disparar em toda escrita da tabela o faria reescrever a coluna em
|
|
19
|
+
-- cada troca de plano ou de nome.
|
|
20
|
+
--
|
|
21
|
+
-- ── E por que `markUrl` vem ANTES de `logoUrl` ─────────────────────────────
|
|
22
|
+
-- Tudo que lê esta coluna desenha num SLOT QUADRADO: o seletor do rail, o
|
|
23
|
+
-- escolhedor pós-login, a tela de troca — todos `OrgMark`, de h-5 a h-9. O
|
|
24
|
+
-- Espaço Facial tem as duas artes, e a coluna pegou o wordmark horizontal: no
|
|
25
|
+
-- quadrado ele virou "…aço|", um pedaço do meio da palavra. `markUrl` existe
|
|
26
|
+
-- exatamente para esse lugar.
|
|
27
|
+
-- ---------------------------------------------------------------------------
|
|
28
|
+
|
|
29
|
+
CREATE OR REPLACE FUNCTION app.sync_tenant_logo_url() RETURNS trigger
|
|
30
|
+
LANGUAGE plpgsql AS $$
|
|
31
|
+
BEGIN
|
|
32
|
+
NEW.logo_url := coalesce(
|
|
33
|
+
NEW.settings #>> '{branding,identity,markUrl}', -- a arte QUADRADA
|
|
34
|
+
NEW.settings #>> '{branding,identity,logoUrl}', -- o wordmark, se for só o que há
|
|
35
|
+
NEW.settings #>> '{branding,logo_url}', -- a grafia anterior à 074
|
|
36
|
+
NEW.logo_url -- ninguém disse nada: mantém
|
|
37
|
+
);
|
|
38
|
+
RETURN NEW;
|
|
39
|
+
END $$;
|
|
40
|
+
|
|
41
|
+
DROP TRIGGER IF EXISTS tenants_sync_logo_url ON public.tenants;
|
|
42
|
+
CREATE TRIGGER tenants_sync_logo_url
|
|
43
|
+
BEFORE INSERT OR UPDATE OF settings ON public.tenants
|
|
44
|
+
FOR EACH ROW EXECUTE FUNCTION app.sync_tenant_logo_url();
|
|
45
|
+
|
|
46
|
+
COMMENT ON COLUMN public.tenants.logo_url IS
|
|
47
|
+
'DERIVADA de settings.branding.identity.logoUrl pelo gatilho tenants_sync_logo_url (089). Leitores antigos — v_team_members, o seletor de contas — continuam lendo daqui; quem ESCREVE escreve no documento da marca.';
|
|
48
|
+
|
|
49
|
+
-- Backfill. `set settings = settings` e não `set updated_at = now()`: o gatilho
|
|
50
|
+
-- escuta a COLUNA settings, e tocar outra não o acorda.
|
|
51
|
+
UPDATE public.tenants SET settings = settings
|
|
52
|
+
WHERE settings #>> '{branding,identity,markUrl}' IS NOT NULL
|
|
53
|
+
OR settings #>> '{branding,identity,logoUrl}' IS NOT NULL;
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
-- ---------------------------------------------------------------------------
|
|
2
|
+
-- 090_o_modulo_do_oficio_vizinho_tem_onde_ser_visto.sql
|
|
3
|
+
-- ---------------------------------------------------------------------------
|
|
4
|
+
-- A 079 deu ao ofício uma OFERTA (`app.apps.preset.modules`), e a tela de
|
|
5
|
+
-- Módulos passou a mostrar só o que o ofício DESTA conta oferece. Isso está
|
|
6
|
+
-- certo como padrão e errado como limite: um salão que resolveu vender online
|
|
7
|
+
-- não tem, em lugar nenhum do produto, onde descobrir que `shop` existe. O
|
|
8
|
+
-- catálogo acabou onde o ofício começou.
|
|
9
|
+
--
|
|
10
|
+
-- O que falta não é dado novo — é uma PORTA para o dado que já está lá. Sete
|
|
11
|
+
-- linhas de `app.apps` dizem, cada uma, um ofício e o que ele oferece, e
|
|
12
|
+
-- nenhum cliente consegue lê-las senão para a própria conta: `app_config()`
|
|
13
|
+
-- só devolve os aplicativos que estão em `app.tenant_apps`.
|
|
14
|
+
--
|
|
15
|
+
-- ── Por que uma função nova, e não mais uma chave no app_config() ──────────
|
|
16
|
+
-- `app_config()` é a chamada do BOOT — ela existe para a shell conseguir
|
|
17
|
+
-- desenhar a primeira tela. O catálogo não participa disso: quem o lê é uma
|
|
18
|
+
-- aba de Configurações, aberta por alguém que já entrou. Pendurá-lo ali é
|
|
19
|
+
-- pagar o payload em todo login de todo usuário por uma tela que a maioria
|
|
20
|
+
-- nunca abre.
|
|
21
|
+
--
|
|
22
|
+
-- E há o motivo prosaico: `normalizeAppConfig`, no @fayz-ai/core publicado,
|
|
23
|
+
-- reconstrói a resposta chave a chave e descarta o que não conhece. Uma chave
|
|
24
|
+
-- nova no `app_config()` só chegaria na tela depois de uma release do pacote —
|
|
25
|
+
-- a tela ficaria esperando o trem errado.
|
|
26
|
+
--
|
|
27
|
+
-- ── Por que `mine` vem do servidor ─────────────────────────────────────────
|
|
28
|
+
-- Quem lê precisa saber qual das rubricas é a DELE: é a que abre expandida, e
|
|
29
|
+
-- é a diferença entre "o seu ofício, e o que mais existe" e uma lista de sete
|
|
30
|
+
-- ofícios anônimos. A resposta depende da conta, e o servidor já sabe quem
|
|
31
|
+
-- está perguntando. Devolver o catálogo cru e mandar o cliente cruzá-lo com
|
|
32
|
+
-- `app_config().apps` seria criar a segunda grafia do mesmo fato — que é
|
|
33
|
+
-- exatamente o que a 079 passou a migration inteira desfazendo.
|
|
34
|
+
--
|
|
35
|
+
-- Nada de permissão nova: o catálogo é o que o PRODUTO oferece, igual para
|
|
36
|
+
-- todo mundo. O que a conta tem continua em `app.tenant_plugins`, e quem liga
|
|
37
|
+
-- ou desliga continua sendo `tenant_plugin_set`, que já exige `settings.manage`
|
|
38
|
+
-- e já impõe o teto do plano.
|
|
39
|
+
-- ---------------------------------------------------------------------------
|
|
40
|
+
|
|
41
|
+
CREATE OR REPLACE FUNCTION public.app_catalog()
|
|
42
|
+
RETURNS jsonb
|
|
43
|
+
LANGUAGE sql
|
|
44
|
+
STABLE SECURITY DEFINER
|
|
45
|
+
SET search_path TO ''
|
|
46
|
+
AS $function$
|
|
47
|
+
SELECT coalesce(jsonb_agg(x ORDER BY x ->> 'name'), '[]'::jsonb)
|
|
48
|
+
FROM (
|
|
49
|
+
SELECT jsonb_build_object(
|
|
50
|
+
'id', a.id,
|
|
51
|
+
'name', a.name,
|
|
52
|
+
'vertical_id', a.vertical_id,
|
|
53
|
+
'modules', coalesce(a.preset -> 'modules', '[]'::jsonb),
|
|
54
|
+
'mine', EXISTS (
|
|
55
|
+
SELECT 1 FROM app.tenant_apps ta
|
|
56
|
+
WHERE ta.app_id = a.id
|
|
57
|
+
AND ta.status = 'active'
|
|
58
|
+
AND ta.tenant_id = app.current_tenant_id())
|
|
59
|
+
) AS x
|
|
60
|
+
FROM app.apps a
|
|
61
|
+
-- Sem ofício não é ofício: o control-admin é a casa, não uma rubrica.
|
|
62
|
+
-- Sem oferta não há o que listar — a linha existiria como categoria
|
|
63
|
+
-- vazia, que é uma pergunta sem resposta.
|
|
64
|
+
WHERE a.active
|
|
65
|
+
AND a.vertical_id IS NOT NULL
|
|
66
|
+
-- `jsonb_typeof` antes: um ofício cujo `modules` não for array ainda
|
|
67
|
+
-- não tem oferta, e uma função STABLE não é lugar de levantar exceção.
|
|
68
|
+
AND jsonb_typeof(a.preset -> 'modules') = 'array'
|
|
69
|
+
AND jsonb_array_length(a.preset -> 'modules') > 0
|
|
70
|
+
) c;
|
|
71
|
+
$function$;
|
|
72
|
+
|
|
73
|
+
REVOKE ALL ON FUNCTION public.app_catalog() FROM public;
|
|
74
|
+
GRANT EXECUTE ON FUNCTION public.app_catalog() TO authenticated;
|
|
75
|
+
GRANT EXECUTE ON FUNCTION public.app_catalog() TO service_role;
|
|
76
|
+
|
|
77
|
+
COMMENT ON FUNCTION public.app_catalog() IS
|
|
78
|
+
'O catálogo por OFÍCIO (090): cada aplicativo ativo com id, nome, vertical_id, a oferta dele (preset.modules) e `mine` — se esta conta o roda. Lida pelo marketplace de Módulos para agrupar o que existe por categoria de ofício; o que a conta TEM continua em app_config().plugins.';
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
-- ---------------------------------------------------------------------------
|
|
2
|
+
-- 091_a_conta_diz_o_que_nao_usa_e_a_marca_pinta_a_casa.sql
|
|
3
|
+
-- ---------------------------------------------------------------------------
|
|
4
|
+
-- Eu semeei as cinco contas de comunidade com os mesmos oito módulos — exatamente
|
|
5
|
+
-- o que esta arquitetura existe para não fazer. O founder corrigiu olhando a
|
|
6
|
+
-- tela: Crema Club não vende curso; Airfryer não tem curso nem conversas.
|
|
7
|
+
--
|
|
8
|
+
-- Ditas, não omitidas. Uma linha `disabled` é a conta AFIRMANDO que não usa o
|
|
9
|
+
-- módulo; omitir cairia no padrão do manifesto, que é ligado. E a rubrica some
|
|
10
|
+
-- sozinha: "Programa" não desenha numa casa sem curso, porque uma rubrica sem
|
|
11
|
+
-- filho ativo não é desenhada (ver rail-preset).
|
|
12
|
+
-- ---------------------------------------------------------------------------
|
|
13
|
+
|
|
14
|
+
INSERT INTO app.tenant_plugins (tenant_id, plugin_id, facet, status, source)
|
|
15
|
+
SELECT t.id, p.plugin_id, '', 'disabled', 'manual'
|
|
16
|
+
FROM public.tenants t
|
|
17
|
+
JOIN (VALUES
|
|
18
|
+
('crema-club', 'courses'),
|
|
19
|
+
('airfryer-collective', 'courses'),
|
|
20
|
+
('airfryer-collective', 'conversations')
|
|
21
|
+
) AS p(slug, plugin_id) ON p.slug = t.slug
|
|
22
|
+
ON CONFLICT (tenant_id, plugin_id, facet) DO UPDATE SET status = 'disabled', updated_at = now();
|
|
23
|
+
|
|
24
|
+
-- ── Espaço Renova: canvas marrom, conteúdo claro ───────────────────────────
|
|
25
|
+
-- Quatro tentativas, e as três primeiras erraram no MESMO ponto: eu escurecia
|
|
26
|
+
-- `surface.background` esperando ver o papel de fundo mudar, e ele não mudava.
|
|
27
|
+
--
|
|
28
|
+
-- O motivo está no shell, não na marca. Com a moldura ligada, o que aparece
|
|
29
|
+
-- ATRÁS dela é `bg-sidebar` — `AppShell` pinta o vão com a cor do rail
|
|
30
|
+
-- (`frame && 'md:bg-sidebar'`). Então, para uma marca:
|
|
31
|
+
--
|
|
32
|
+
-- rail.background é o rail E o canvas atrás da moldura
|
|
33
|
+
-- surface.background é a superfície DENTRO da moldura, onde mora o texto
|
|
34
|
+
--
|
|
35
|
+
-- Com isso o pedido é direto: o marrom vai no rail, o conteúdo volta a ser
|
|
36
|
+
-- claro com tinta escura, e nenhum texto fica sobre um fundo da própria cor.
|
|
37
|
+
-- A primária é o marrom AMOSTRADO da arte oficial do cliente (#935427), não uma
|
|
38
|
+
-- interpretação minha do logo. O creme da arte (#FFFAEA) ficou só no ladrilho:
|
|
39
|
+
-- ele é a cor do desenho, não a da casa.
|
|
40
|
+
--
|
|
41
|
+
-- E `surface` SAI do documento em vez de receber um branco escolhido por mim.
|
|
42
|
+
-- Sem a chave, a marca não opina sobre a superfície e o app usa o tema dele —
|
|
43
|
+
-- que é diferente de eu escrever outro hex claro, porque qualquer hex seria
|
|
44
|
+
-- mais uma opinião, e o pedido era não ter opinião nenhuma ali.
|
|
45
|
+
--
|
|
46
|
+
-- ── Armadilha que me custou três recargas ─────────────────────────────────
|
|
47
|
+
-- O tema lê a marca do SNAPSHOT DA ORGANIZAÇÃO (`fayz:org-snapshot`), não do
|
|
48
|
+
-- snapshot de configuração (`fayz:app-config:<tenant>`). Limpar um não limpa o
|
|
49
|
+
-- outro, e eu fiquei vendo o creme antigo depois de já o ter removido do banco.
|
|
50
|
+
-- Mesma família do TTL de 5 min que já escondeu mudança de trial antes.
|
|
51
|
+
UPDATE public.tenants SET settings = jsonb_set(settings, '{branding,tokens}', $$
|
|
52
|
+
{
|
|
53
|
+
"color": { "primary": "#935427", "accent": "#B0743F" },
|
|
54
|
+
"rail": { "background": "#2A201A", "foreground": "#F7F2EC", "border": "#3E3125",
|
|
55
|
+
"accent": "#3E3125", "accentForeground": "#D9A87C", "muted": "#B3A192" },
|
|
56
|
+
"radius": "0.75rem"
|
|
57
|
+
}
|
|
58
|
+
$$::jsonb), updated_at = now()
|
|
59
|
+
WHERE name = 'Espaco Renova Rio';
|
|
60
|
+
|
|
61
|
+
-- O ladrilho quadrado, agora sobre o marrom da marca em vez de preto.
|
|
62
|
+
UPDATE public.tenants t SET settings = jsonb_set(t.settings, '{branding,identity}',
|
|
63
|
+
coalesce(t.settings #> '{branding,identity}', '{}'::jsonb) || jsonb_build_object(
|
|
64
|
+
'logoUrl', 'https://klvfxzreepavcpyjiwla.supabase.co/storage/v1/object/public/avatars/947b0ed0-82ad-4b99-88d0-b503a7ad09c2/branding/tile.png',
|
|
65
|
+
'markUrl', 'https://klvfxzreepavcpyjiwla.supabase.co/storage/v1/object/public/avatars/947b0ed0-82ad-4b99-88d0-b503a7ad09c2/branding/tile.png')
|
|
66
|
+
), updated_at = now()
|
|
67
|
+
WHERE t.name = 'Espaco Renova Rio';
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
-- A tabela de ramos aprende o vocabulário que a conta passou a falar
|
|
2
|
+
--
|
|
3
|
+
-- A 093 fechou `tenants.vertical_id` em sete nomes e deixou órfão um caminho
|
|
4
|
+
-- que ninguém olhou: o gatilho `tenants_apply_vertical_defaults`, que roda a
|
|
5
|
+
-- cada INSERT em `public.tenants` e cruza `app.vertical_defaults` POR ESSE
|
|
6
|
+
-- MESMO NOME. A tabela de ramos fala o vocabulário antigo:
|
|
7
|
+
--
|
|
8
|
+
-- app.vertical_defaults agency, ecommerce, restaurant, salon, school
|
|
9
|
+
-- tenants.vertical_id food, beauty, commerce, education, events,
|
|
10
|
+
-- accounting, generic
|
|
11
|
+
--
|
|
12
|
+
-- A interseção é VAZIA. Desde a 093, toda conta nova nasce sem módulo nenhum
|
|
13
|
+
-- semeado — e em silêncio, porque o gatilho engole a falha de propósito (uma
|
|
14
|
+
-- conta sem módulos se conserta num clique; uma conta que não foi criada é um
|
|
15
|
+
-- cadastro perdido). O sintoma some no warning e a conta chega vazia.
|
|
16
|
+
--
|
|
17
|
+
-- Isto não reabre a decisão da 093. `preset.modules` cruzado com
|
|
18
|
+
-- `tenant_plugins` continua sendo quem decide o que aparece; esta tabela é só
|
|
19
|
+
-- o ponto de partida de quem acabou de nascer. O conserto é traduzir as
|
|
20
|
+
-- dezesseis linhas para o vocabulário fechado, não criar uma oitava grafia.
|
|
21
|
+
--
|
|
22
|
+
-- restaurant -> food salon -> beauty school -> education
|
|
23
|
+
-- ecommerce -> commerce agency -> generic
|
|
24
|
+
--
|
|
25
|
+
-- `events` e `accounting` ficam sem linha, e isso é a resposta honesta: a
|
|
26
|
+
-- bilheteria e a contabilidade nunca tiveram default nenhum aqui, e inventar
|
|
27
|
+
-- um agora seria adivinhar.
|
|
28
|
+
--
|
|
29
|
+
-- Idempotente: insere o traduzido e só então apaga o original.
|
|
30
|
+
|
|
31
|
+
INSERT INTO app.vertical_defaults (vertical_id, plugin_id, facet, note)
|
|
32
|
+
SELECT v.novo, d.plugin_id, d.facet, d.note
|
|
33
|
+
FROM app.vertical_defaults d
|
|
34
|
+
JOIN (VALUES
|
|
35
|
+
('restaurant', 'food'),
|
|
36
|
+
('salon', 'beauty'),
|
|
37
|
+
('ecommerce', 'commerce'),
|
|
38
|
+
('school', 'education'),
|
|
39
|
+
('agency', 'generic')
|
|
40
|
+
) AS v(velho, novo) ON v.velho = d.vertical_id
|
|
41
|
+
ON CONFLICT (vertical_id, plugin_id, facet) DO NOTHING;
|
|
42
|
+
|
|
43
|
+
DELETE FROM app.vertical_defaults
|
|
44
|
+
WHERE vertical_id IN ('restaurant','salon','ecommerce','school','agency');
|
|
45
|
+
|
|
46
|
+
-- O que sobrou fala o vocabulário fechado, e nada mais.
|
|
47
|
+
DO $$
|
|
48
|
+
DECLARE v_fora text[];
|
|
49
|
+
BEGIN
|
|
50
|
+
SELECT array_agg(DISTINCT vertical_id) INTO v_fora
|
|
51
|
+
FROM app.vertical_defaults
|
|
52
|
+
WHERE vertical_id NOT IN
|
|
53
|
+
('food','beauty','commerce','education','events','accounting','generic');
|
|
54
|
+
IF v_fora IS NOT NULL THEN
|
|
55
|
+
RAISE EXCEPTION 'app.vertical_defaults ainda fala fora do vocabulario: %', v_fora;
|
|
56
|
+
END IF;
|
|
57
|
+
END $$;
|
|
58
|
+
|
|
59
|
+
COMMENT ON TABLE app.vertical_defaults IS
|
|
60
|
+
'O que uma conta NOVA daquele ramo já nasce com. Mesmo vocabulário de tenants.vertical_id (093). Ponto de partida, não portão: quem decide o que aparece é app.apps.preset.modules cruzado com app.tenant_plugins.';
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
-- ---------------------------------------------------------------------------
|
|
2
|
+
-- 108_o_erp_generico_nao_vende_por_pedido.sql
|
|
3
|
+
-- ---------------------------------------------------------------------------
|
|
4
|
+
-- O ControlGroup é o ERP genérico: ele roda o `full` e o `fiscal`, e a empresa
|
|
5
|
+
-- atendida por ele não vende por PEDIDO. A venda dela é orçamento, contrato e
|
|
6
|
+
-- título — o funil do CRM e o razão do financeiro. "Pedidos" no rail é uma
|
|
7
|
+
-- tela que ninguém daquela casa abre, e que ainda promete um fluxo de balcão
|
|
8
|
+
-- que não existe ali.
|
|
9
|
+
--
|
|
10
|
+
-- ── Por que a linha está lá, se o ofício não a oferece ─────────────────────
|
|
11
|
+
-- `app.apps.preset.modules` do `full` e do `fiscal` não citam `orders`. O
|
|
12
|
+
-- portão da oferta (resolvePluginRuntime) pularia o plugin por isso — MENOS
|
|
13
|
+
-- quando existe uma linha `active` explícita, que é o caso do módulo vendido à
|
|
14
|
+
-- parte e vence a oferta. É uma dessas linhas, resíduo de quando a conta
|
|
15
|
+
-- recebeu aplicativo que não era o dela, que está desenhando "Pedidos".
|
|
16
|
+
--
|
|
17
|
+
-- ── Por que aqui, e não no preset do ofício ────────────────────────────────
|
|
18
|
+
-- Tirar `orders` de `app.apps.preset` tiraria Pedidos de TODA conta que roda o
|
|
19
|
+
-- mesmo aplicativo. O que muda é o que ESTA conta usa, e isso mora na camada
|
|
20
|
+
-- do meio: o ofício propõe, a conta dispõe.
|
|
21
|
+
--
|
|
22
|
+
-- ── Dita, não omitida ──────────────────────────────────────────────────────
|
|
23
|
+
-- Apagar a linha devolveria o plugin ao padrão do manifesto, que é
|
|
24
|
+
-- `defaultEnabled: true`. O runtime é aditivo: silêncio é "sim". Só um
|
|
25
|
+
-- `disabled` explícito é a conta AFIRMANDO que não usa o módulo.
|
|
26
|
+
--
|
|
27
|
+
-- ── É `orders`, não `shop` ─────────────────────────────────────────────────
|
|
28
|
+
-- Os dois plugins desenham `/orders` e os dois chamam a linha de "Pedidos", e
|
|
29
|
+
-- desligar o errado não mudaria nada na tela. Quem desenha aqui é `orders`:
|
|
30
|
+
-- `shop` contribui QUATRO rubricas juntas — Pedidos, Produtos, Conteúdo e
|
|
31
|
+
-- Descontos —, e é assim que ele aparece na PULSE. O rail do ControlGroup não
|
|
32
|
+
-- tem vitrine nenhuma; `orders` contribui uma linha só, e é a linha que sobra.
|
|
33
|
+
--
|
|
34
|
+
-- O Cardápio vai junto, e de propósito: `orders` declara `dependencies:
|
|
35
|
+
-- ['menu']` e o runtime acende a dependência por arrasto. Sem quem o puxasse,
|
|
36
|
+
-- `menu` volta a ficar fora da oferta e sai do rail sozinho.
|
|
37
|
+
--
|
|
38
|
+
-- ── Escrito na tabela, e não por `tenant_plugin_set` ───────────────────────
|
|
39
|
+
-- A porta é a certa para o produto: ela exige `settings.manage` e impõe o teto
|
|
40
|
+
-- do plano. Uma migration roda sem usuário — não há a quem pedir permissão —,
|
|
41
|
+
-- então ela escreve a mesma linha que a porta escreveria, como fez a 105.
|
|
42
|
+
-- ---------------------------------------------------------------------------
|
|
43
|
+
|
|
44
|
+
-- Por `slug`: num banco vazio nenhuma linha casa e a migration não faz nada,
|
|
45
|
+
-- em vez de estourar procurando uma conta que ainda não nasceu.
|
|
46
|
+
INSERT INTO app.tenant_plugins (tenant_id, plugin_id, facet, status, source)
|
|
47
|
+
SELECT t.id, 'orders', '', 'disabled', 'manual'
|
|
48
|
+
FROM public.tenants t
|
|
49
|
+
WHERE t.slug = 'controlgroup'
|
|
50
|
+
ON CONFLICT (tenant_id, plugin_id, facet) DO UPDATE SET status = 'disabled', updated_at = now();
|