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 0000000..b038611 Binary files /dev/null and b/saida.md differ 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 @@
-