From be4e766f9bd1d427cee322de32bf75e7a3903a01 Mon Sep 17 00:00:00 2001 From: Ricardo Carneiro Date: Thu, 30 Apr 2026 19:35:32 -0300 Subject: [PATCH] fix: aparencia diferenste para os tenants --- .claude/settings.local.json | 16 +- .gitea/workflows/deploy-bcards.yml | 47 +++++- CLAUDE.md | 44 +++++- saida.md | Bin 0 -> 74294 bytes src/BCards.Web/Program.cs | 56 +++---- src/BCards.Web/Views/Shared/_Layout.cshtml | 13 +- src/BCards.Web/appsettings.Development.json | 50 +++--- src/BCards.Web/appsettings.Luslinks.json | 7 + src/BCards.Web/appsettings.Spicylinks.json | 159 +++++++++++++++++--- src/BCards.Web/appsettings.json | 3 + src/BCards.Web/wwwroot/css/site.css | 6 +- 11 files changed, 315 insertions(+), 86 deletions(-) create mode 100644 saida.md diff --git a/.claude/settings.local.json b/.claude/settings.local.json index c76a4e2..06987e0 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -36,7 +36,21 @@ "Bash(lsof:*)", "Bash(dotnet run:*)", "Bash(dotnet user-secrets:*)", - "Bash(xargs grep:*)" + "Bash(xargs grep:*)", + "mcp__stripe__list_products", + "mcp__stripe__list_prices", + "mcp__stripe__get_stripe_account_info", + "mcp__stripe__search_stripe_resources", + "Bash(docker exec:*)", + "mcp__stripe__list_subscriptions", + "mcp__stripe__create_product", + "mcp__stripe__create_price", + "Bash(git push:*)", + "mcp__stripe__stripe_api_execute", + "mcp__stripe__stripe_api_search", + "Bash(ctx csharp:*)", + "Bash(ctx auto:*)", + "Bash(git log:*)" ] }, "enableAllProjectMcpServers": false diff --git a/.gitea/workflows/deploy-bcards.yml b/.gitea/workflows/deploy-bcards.yml index 22be7c3..0d1e230 100644 --- a/.gitea/workflows/deploy-bcards.yml +++ b/.gitea/workflows/deploy-bcards.yml @@ -254,7 +254,10 @@ jobs: "CtaDescription": "Junte-se a milhares de profissionais que já têm sua presença digital organizada no BCards.", "CtaButtonText": "Criar Minha Página Grátis", "MetaKeywords": "cartão digital, página de links, bio links, linktree brasil, página profissional, corretor, advogado, médico, consultor", - "FooterTagline": "Sua presença digital profissional, simplificada." + "FooterTagline": "Sua presença digital profissional, simplificada.", + "HeroGradient": "linear-gradient(135deg, #667eea 0%, #764ba2 100%)", + "PrimaryColor": "#667eea", + "PrimaryColorDark": "#5a6fd6" }, "Support": { "TelegramUrl": "https://t.me/jobmakerbr", @@ -399,7 +402,30 @@ jobs: "CtaDescription": "Milhares de criadoras já centralizam seus links e aumentam suas conversões com o SpicyLinks.", "CtaButtonText": "Criar Minha Bio", "MetaKeywords": "bio links criadora, creator bio, linktree conteudo adulto, links onlyfans, bio instagram criadora", - "FooterTagline": "Seu conteúdo, sua identidade." + "FooterTagline": "Seu conteúdo, sua identidade.", + "HeroGradient": "linear-gradient(135deg, #ff416c 0%, #c0392b 100%)", + "PrimaryColor": "#e63946", + "PrimaryColorDark": "#c1121f", + "DefaultCategories": [ + { "Icon": "📸", "Name": "Modelos", "Slug": "modelos", "Description": "Modelos e criadores de conteúdo visual", "SeoKeywords": [ "modelo", "fotografia", "conteúdo", "criadora" ] }, + { "Icon": "⭐", "Name": "Influencers", "Slug": "influencers","Description": "Influencers e personalidades digitais", "SeoKeywords": [ "influencer", "digital", "social media" ] }, + { "Icon": "💪", "Name": "Fitness", "Slug": "fitness", "Description": "Criadores de conteúdo fitness e lifestyle", "SeoKeywords": [ "fitness", "academia", "saúde", "corpo" ] }, + { "Icon": "🎨", "Name": "Arte", "Slug": "arte", "Description": "Artistas e criadores de conteúdo visual", "SeoKeywords": [ "arte", "ilustração", "design", "criativo" ] }, + { "Icon": "🎵", "Name": "Música", "Slug": "musica", "Description": "Músicos e cantores independentes", "SeoKeywords": [ "música", "cantor", "artista", "show" ] }, + { "Icon": "🎮", "Name": "Gaming", "Slug": "gaming", "Description": "Streamers e criadores de conteúdo gamer", "SeoKeywords": [ "gaming", "streamer", "games", "twitch" ] }, + { "Icon": "🦸", "Name": "Cosplay", "Slug": "cosplay", "Description": "Cosplayers e criadores de fantasia", "SeoKeywords": [ "cosplay", "anime", "fantasia", "cosplayer" ] }, + { "Icon": "💋", "Name": "Lifestyle", "Slug": "lifestyle", "Description": "Criadores de conteúdo lifestyle e entretenimento", "SeoKeywords": [ "lifestyle", "entretenimento", "diversão" ] } + ], + "AllowedLinkTypes": [ + { "Icon": "fas fa-globe", "Label": "🌐 Site Geral", "Prefix": "https://", "Placeholder": "exemplo.com", "Instructions": "Digite o domínio e caminho", "Color": "bg-primary" }, + { "Icon": "fas fa-envelope", "Label": "✉️ Email", "Prefix": "mailto:", "Placeholder": "seuemail@exemplo.com", "Instructions": "Digite apenas o email", "Color": "bg-success" }, + { "Icon": "fas fa-phone", "Label": "📞 Telefone", "Prefix": "tel:", "Placeholder": "5511999999999", "Instructions": "Número com código do país", "Color": "bg-success" }, + { "Icon": "fab fa-instagram", "Label": "📸 Instagram", "Prefix": "https://instagram.com/","Placeholder": "seu.usuario", "Instructions": "Digite apenas seu usuário", "Color": "bg-danger" }, + { "Icon": "fab fa-twitter", "Label": "🐦 Twitter/X", "Prefix": "https://x.com/", "Placeholder": "seu_usuario", "Instructions": "Digite apenas seu usuário", "Color": "bg-dark" }, + { "Icon": "fab fa-tiktok", "Label": "🎵 TikTok", "Prefix": "https://tiktok.com/@", "Placeholder": "seu.usuario", "Instructions": "Digite apenas seu usuário", "Color": "bg-dark" }, + { "Icon": "fas fa-shopping-cart","Label": "🛒 Lista de Desejos","Prefix": "https://", "Placeholder": "wishlist.com/...", "Instructions": "Link para lista de desejos", "Color": "bg-warning" }, + { "Icon": "fas fa-heart", "Label": "❤️ Assinatura", "Prefix": "https://", "Placeholder": "plataforma.com/...", "Instructions": "Link para plataforma paga", "Color": "bg-danger" } + ] }, "Support": { "TelegramUrl": "https://t.me/jobmakerbr", @@ -544,7 +570,22 @@ jobs: "CtaDescription": "Líderes de toda denominação já usam o LusLinks para alcançar mais pessoas com sua mensagem de fé.", "CtaButtonText": "Criar Minha Bio de Fé", "MetaKeywords": "bio links pastor, página ministério, linktree cristão, links religiosos, página iglesia, bio pastor, links igreja", - "FooterTagline": "Conectando fé e comunidade." + "FooterTagline": "Conectando fé e comunidade.", + "HeroGradient": "linear-gradient(135deg, #5b9bd5 0%, #1a5276 100%)", + "PrimaryColor": "#2471a3", + "PrimaryColorDark": "#1a5276", + "AllowedLinkTypes": [ + { "Icon": "fas fa-globe", "Label": "🌐 Site / Ministério", "Prefix": "https://", "Placeholder": "ministerio.com.br", "Instructions": "Digite o domínio do site", "Color": "bg-primary" }, + { "Icon": "fas fa-envelope", "Label": "✉️ Email", "Prefix": "mailto:", "Placeholder": "contato@ministerio.com", "Instructions": "Digite apenas o email", "Color": "bg-success" }, + { "Icon": "fas fa-phone", "Label": "📞 Telefone / WhatsApp", "Prefix": "tel:", "Placeholder": "5511999999999", "Instructions": "Número com código do país", "Color": "bg-success" }, + { "Icon": "fab fa-youtube", "Label": "📺 YouTube", "Prefix": "https://youtube.com/", "Placeholder": "@canal ou c/CANAL", "Instructions": "Digite o canal ou @usuário", "Color": "bg-danger" }, + { "Icon": "fab fa-instagram", "Label": "📸 Instagram", "Prefix": "https://instagram.com/", "Placeholder": "seu.usuario", "Instructions": "Digite apenas seu usuário", "Color": "bg-danger" }, + { "Icon": "fas fa-book", "Label": "📖 Estudo / Série", "Prefix": "https://", "Placeholder": "link-do-estudo.com", "Instructions": "Link para estudo bíblico ou série", "Color": "bg-info" }, + { "Icon": "fas fa-calendar", "Label": "📅 Agenda / Eventos", "Prefix": "https://", "Placeholder": "calendly.com/seunome", "Instructions": "Link para agenda ou evento", "Color": "bg-warning" }, + { "Icon": "fas fa-donate", "Label": "🙏 Dízimos / Ofertas", "Prefix": "https://", "Placeholder": "pix.com.br/ministerio", "Instructions": "Link para doações ou dízimos", "Color": "bg-success" }, + { "Icon": "fas fa-map-marker-alt","Label": "📍 Localização", "Prefix": "https://maps.google.com/?q=", "VisualPrefix": "📍 Maps:", "Placeholder": "Rua da Igreja, 123", "Instructions": "Endereço da igreja/ministério", "Color": "bg-warning" }, + { "Icon": "fas fa-download", "Label": "⬇️ Material / Apostila","Prefix": "https://", "Placeholder": "drive.google.com/...", "Instructions": "Link para download de material", "Color": "bg-secondary" } + ] }, "Support": { "TelegramUrl": "https://t.me/jobmakerbr", diff --git a/CLAUDE.md b/CLAUDE.md index 384ee7c..2c157bd 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -243,4 +243,46 @@ if (page.Status == PageStatus.Creating || page.Status == PageStatus.Rejected) } ``` -This architecture supports a production-ready SaaS application with complex business rules, payment integration, and content moderation workflows. \ No newline at end of file +This architecture supports a production-ready SaaS application with complex business rules, payment integration, and content moderation workflows. + + +# Project instructions + +## ctx — use before reading files + +This project has `ctx` available in PATH. Use it to understand the codebase **before** reading files directly. It produces compact markdown summaries that cost far fewer tokens than raw file content. + +### When to use + +**Always use `ctx` first** when you need to: +- Understand project structure → `ctx csharp project` or `ctx react project` +- Understand a file's structure → `ctx csharp outline ` or `ctx react outline ` +- Check build errors → `ctx csharp errors` or `ctx react errors` +- Understand git state → `ctx git` +- Detect what stack this project uses → `ctx auto detect` + +### Workflow + +1. **Start of session:** run `ctx auto detect` to see what's here, then `ctx project` for an overview. +2. **Before reading a file:** run `ctx outline ` first. Only read the full file if the outline isn't enough (e.g., you need to see method body logic). +3. **After making changes:** run `ctx errors` instead of `dotnet build` or `tsc` — the output is pre-filtered to only relevant diagnostics. +4. **Before committing:** run `ctx git` for a compact diff summary. + +### Available commands + +``` +ctx auto detect # detect stack(s) in current directory +ctx auto project # run project summary for all detected stacks + +ctx csharp project # .NET solution overview (projects, refs, packages) +ctx csharp outline # file structure without method bodies +ctx csharp errors # filtered dotnet build output (errors + top warnings) + +ctx git # branch, status, recent commits, diff summary +``` + +### Important + +- `ctx` output is a **summary**, not the full picture. If you need implementation details (method bodies, exact logic, specific lines), read the file directly. +- Do not run `ctx` commands that don't match the project stack. Use `ctx auto detect` if unsure. +- `ctx csharp errors` assumes `dotnet restore` was already run. If you get restore errors, run `dotnet restore` first, then `ctx csharp errors`. \ No newline at end of file diff --git a/saida.md b/saida.md new file mode 100644 index 0000000000000000000000000000000000000000..b038611e51709d56303aa9ceb0b61d98622ded79 GIT binary patch literal 74294 zcmeI5&5j$#5y$%+AWx7B4{IO+R>G3p&toiKg4hUPJH7-utYk%2ENK<36eN$4M=)}8 z9wE64x$6;f$t6H^&@oQ^rqr3CW~Zx$!=Xq_fmo3uYkGQ$oPYi6qpN@T?@!Wi(nUH- zPvp6NeLvk#FVl6pOb^q8#`|}x*GoC#Nt(TTeJZbCr+=pVA8~yC?9+55M}I9xe=U;F z()094#(b2X%80Y{Tlw_4JTKB|I!|Zv>WRFA^kX^ekJFE;zkG(X>I`qvca5{_x%88_ z=~a9ZI!N(5cpW+rEZWcC!JG8g28w}!tN8ae{j2f$Og<4&0 zOLyKO`z(DaufCP{p31ww{=apZrO)NPXO%^GA)nlmzmLc%&>>2K~$`dh$!*jt0;dznT~Vj7MapjTkkHa zeK79qb*^HTek$JE7wMDqvo%Kex!iv*#H0Iz{JoZ#)%VUZ+P}fSY}mGU>EiB3ZL7c8 zcg8t$j5>7<{HJk$x9^`OhR@`Fyp}stWAL}qJ$Ze(a(~{8{Ti*gs_go;L~f4d?^N#S z{NNktI#Oj$(xFJ4$)BQ8NqQlUzspyO4|wxshA)+(gNnwPSs^`P+E^x%BN2g}avD!&7-Y5X4^n zZc9hLf0puxw>|EK}qD##>u4;9d-HuWBM+1HA+(W^%+Rnj6|5#b8)!)vl zZQK+V+Ey#0;X>?+Px?aa&zVTiO^Z0I#ypg9m-5O+53ePvc2BrIbv{o84RgHD!owk)AI?nGj|YXQ5jw08>)e4^%Xhr|RF1L` zZZ7e#xN57i1$qD3`aXl}Tb^oDqa|%N<#^pK z8(hDHVy|w9@1e`!`cu|GAKRu;tmhIdCpJaYz0R5397)l5)=(^5h^3&WwfoLjj5rh9 zfJMQwSkm|dbCD)e=CLS;6%)rhu;WMA*dviV5z9t|&2xlafyT46g122woeQAMXddE}Y!_timti_!jSn!xP!S2U)nT znj_Aud4RqiJ!NcMN<8Gm#P*@s82q2gm-eG%kG3h@J&)l3WvkZybm}Poetme2)KkfJ z^s#g6#^dGQUd#nw%1BW&GSJA5hh#iF5ybLHFDnlL zKfy-Nnc#6|{XQJexlhZp2|UbE-KpQV!Q{r#Kp#9@t$EyDEy&!-v$V<(j&Eli=fXIN zX)LSfGS*V%NDhGr%hDCm;0WqQG+){$1~Qiz$eA2jo>Aap;9_WCObx7$i@!`?rO#X_ zv6(tKI9eGk;C@uR;jw~Amh@7j6dO;?v5})Ejr#E(q@zj>R;jh=o+Fg&9>}b2%Xutp z4{RLsz8Hsv#HjnEf%UQRcT!D!Z)?~{#hS<0MNZerSiEZ{D5{Y{@XM+z0O_bhfDL-q zr4U(!H?qFR%QQ1KE(9CLubqqdVl$f9ejQ%=)HTot8$U$81-!VZc%a1s656V?2oEND=&iAeI|cY_&BPLkRocuCznQU#gZenA;vpHDGC~I)j(IPH*BBb;gNN*P0XO_g?%P{} z5Xx=JcknQtIImEM)yEc6=v=gB+SC1;xFCKrnQDtwYMmnd(_3}O5yTSxD`GNFq)rje zUDj&gV&GzEU`!44!Nm{36SdxtwasOu09WezZBIOrUUT5wk>F5yZ0+%&UFYU5|5?}j z=@I??=Tkv!Yeuop0ll~Eyr*r z>QA4$5Pul|&k+t^kgmx&!TfkSa_&<>l?JrIxfhSI^@nJ@$hTn5UlmFX2 zMS}m2$^8NuLIZuU@k8)`#fv_B9)LzxMvPIlo`5K^EzQnpaC-2_idoF6hzKzhYO!o- z=91&Y7D5Le1|Eh6#?-*B;~}^La@oueTmiG_S7aj`Ym{PGU<%mDfCB`cF_9QC&=J+Ow@ zH~RJ2E8dv7@_p*-%2zqmzD1vwHV!u52~3>22Kr#)y77-8eepAYvl%?=Zow=f6h8D+ zX;$8rQAqo=fSzPDEzHvOWgs*C4s7(S@z9RM1Rjp*`$jZ0G|&eRwZrLEwL{=7*$MYr z)>e3OK%3!+#91xe(ymohdC;5GmZqyHYa~273sPxJ#tcldXQI8Uy_Lo_Cc-Vt^A1c5 zObiW-sewM2xGqN=(t`jVr#@&IDU?g%sZ!%>vpx`3eP(;_R4N!)AEExZjkR>EQfXgL znd~!DuUEdxp?DZE;U}Wu_MXk(1Re$+h6YL+*ajZ1&lABGP>5B|Hm+zbFWqTvo=66ZI0cAhAspCtE>h&a z&Rmgj)=h(MoWZxUF!MxuciX!l1Wz0@E*RcpXkZ(-IGiWi9cUC+)PMGMh0XmK>Hc72 zE;tI-`8xjS%4s6QHV0+qT2E!~_ZoN@co-TOQv>^fhn~Huz!flyT2LF|$fw~6gIw0y z3{aMu0Y>+&v*ieI7QT4lVwRaFvj5fi{TL%>7CEyn?{S<+V%OQ4?~ill7CNtEObmpI5VduloLOGW;XfkVIC{z1E5wI#|FCgk#ZmY|>>HA^hrmlfqw{hgm@N4W!Ecm$A;bZG8i%8EgjVRh(#I#!fTto(NtEk?D+BkM@E6+b{Tt@it*J_3p?(B=<*PKtL!?6)mdD9>SsB9_N?k=MWXh+wxwASLmU)4XZcv#rw;o`HH#H>%(jhsO1t0FP}>%G z7OC~06Dc({pmH~sL?j%($vbJ|bN!dmh?THi-qx+P8D3H$`QKG}5@AY$IP#KPL4 zcxXj|6rH=XJmyQmRI65Wzgl6yVM8Q--d15KG*j}zv#(C z^vmYsaD6-jGCe+NY#WxymIep&no`Sd4z+E;^_%F|ejRu?bq!1z4|AT`LLs6V8vCGg zlSleYjsR=fwZy{>Gagz|n83r#>ZX3*29p~{1KYsEA+ZmQ*HTsE!4>s6?MhS-4(aYA!%v8R=FLgX&-^Q}yVc(jXz57wwf|Wm=I=YYJ5AD;ayKdhU`|+^644c{+ zYZW2(O?ON4?AD8A%f8;iwdPQFa?sAZ{c2UI=Q)-uC|~VRA1-42fs3Jmk_Pqz7d>6F za>RjJNDqneGftmQp^+0DF+SxB=ATiL%AwQq8Mu(~{U;9kVY zt^pzPz@yve%jgEk zH6Zp*IDv;_-V6KW-^E={-*_pu7<{Lr`$xqto#)IfLs literal 0 HcmV?d00001 diff --git a/src/BCards.Web/Program.cs b/src/BCards.Web/Program.cs index 22ba835..3caee33 100644 --- a/src/BCards.Web/Program.cs +++ b/src/BCards.Web/Program.cs @@ -69,34 +69,35 @@ if (isDevelopment) if (!string.IsNullOrEmpty(openSearchUrl)) { var indexFormat = "b-cards-dev-{0:yyyy-MM}"; + Console.WriteLine($"[OPENSEARCH DEV] Configurando sink → {openSearchUrl}"); - try + // TODO: após confirmar que conecta, restaurar try/catch silencioso (opcional) + loggerConfig.WriteTo.Async(a => a.OpenSearch(new OpenSearchSinkOptions(new Uri(openSearchUrl)) { - loggerConfig.WriteTo.Async(a => a.OpenSearch(new OpenSearchSinkOptions(new Uri(openSearchUrl)) + IndexFormat = indexFormat, + AutoRegisterTemplate = false, + ModifyConnectionSettings = conn => conn + .RequestTimeout(TimeSpan.FromSeconds(5)) + .PingTimeout(TimeSpan.FromSeconds(3)), + MinimumLogEventLevel = LogEventLevel.Debug, + EmitEventFailure = EmitEventFailureHandling.WriteToSelfLog, + RegisterTemplateFailure = RegisterTemplateRecovery.IndexAnyway, + BatchPostingLimit = 10, + Period = TimeSpan.FromSeconds(2), + TemplateCustomSettings = new Dictionary { - IndexFormat = indexFormat, - AutoRegisterTemplate = false, // Não faz GET / no startup - ModifyConnectionSettings = conn => conn - .RequestTimeout(TimeSpan.FromSeconds(5)) - .PingTimeout(TimeSpan.FromSeconds(3)), - MinimumLogEventLevel = LogEventLevel.Debug, - EmitEventFailure = EmitEventFailureHandling.WriteToSelfLog, - RegisterTemplateFailure = RegisterTemplateRecovery.IndexAnyway, - BatchPostingLimit = 10, - Period = TimeSpan.FromSeconds(2), - TemplateCustomSettings = new Dictionary - { - {"number_of_shards", "1"}, - {"number_of_replicas", "0"} - } - }), - bufferSize: 10000, - blockWhenFull: false); - } - catch (Exception) - { - // Falha silenciosa - logs continuam no console e arquivo - } + {"number_of_shards", "1"}, + {"number_of_replicas", "0"} + } + }), + bufferSize: 10000, + blockWhenFull: false); + + Console.WriteLine($"[OPENSEARCH DEV] Sink registrado. Erros de envio aparecem como [SERILOG SELF] no console."); + } + else + { + Console.WriteLine("[OPENSEARCH DEV] OpenSearchUrl não configurado — sem sink OpenSearch."); } } else @@ -151,9 +152,10 @@ else Period = TimeSpan.FromSeconds(5), }), bufferSize: 10000, blockWhenFull: false); } - catch (Exception) + catch (Exception ex) { - // Falha silenciosa em produção - logs continuam no console/arquivo + // OpenSearch é opcional — app continua com console/arquivo + Console.WriteLine($"[OPENSEARCH PROD] Falha ao configurar sink → {openSearchUrl}: {ex.Message}"); } } } diff --git a/src/BCards.Web/Views/Shared/_Layout.cshtml b/src/BCards.Web/Views/Shared/_Layout.cshtml index c4e080e..b46bbfb 100644 --- a/src/BCards.Web/Views/Shared/_Layout.cshtml +++ b/src/BCards.Web/Views/Shared/_Layout.cshtml @@ -51,8 +51,6 @@ --tenant-gradient: @tenant.HeroGradient; } .hero-section { background: var(--tenant-gradient) !important; } - .bg-home-blue { background: var(--tenant-gradient) !important; } - .bg-home-blue .navbar-collapse { background-color: color-mix(in srgb, var(--tenant-primary) 80%, black) !important; } .btn-primary { background-color: var(--tenant-primary) !important; border-color: var(--tenant-primary) !important; } .btn-primary:hover { background-color: var(--tenant-primary-dark) !important; border-color: var(--tenant-primary-dark) !important; } .btn-outline-primary { color: var(--tenant-primary) !important; border-color: var(--tenant-primary) !important; } @@ -101,14 +99,14 @@ 100% { background-position: 200% 0; } } - /* Destacar item ativo do menu */ + /* Destacar item ativo do menu — usa cor do tenant */ .nav-link.active { - background-color: rgba(0, 123, 255, 0.1) !important; + background-color: color-mix(in srgb, var(--tenant-primary, #0d6efd) 10%, transparent) !important; border-radius: 6px !important; font-weight: 600 !important; } - - /* Para homepage (fundo azul) */ + + /* Para homepage (fundo colorido) */ .bg-home-blue .nav-link.active { background-color: rgba(255, 255, 255, 0.2) !important; } @@ -159,7 +157,8 @@
-