Release/ArtigosPDF #21

Merged
ricardo merged 6 commits from Release/ArtigosPDF into main 2025-11-07 14:55:49 +00:00
3 changed files with 216 additions and 17 deletions
Showing only changes of commit 37cd753a6a - Show all commits

View File

@ -30,7 +30,11 @@
"Bash(ssh:*)", "Bash(ssh:*)",
"Bash(cat:*)", "Bash(cat:*)",
"Bash(dig:*)", "Bash(dig:*)",
"Bash(git commit:*)" "Bash(git commit:*)",
"Bash(netstat:*)",
"Bash(ss:*)",
"Bash(lsof:*)",
"Bash(dotnet run:*)"
] ]
}, },
"enableAllProjectMcpServers": false "enableAllProjectMcpServers": false

View File

@ -535,7 +535,7 @@
var twitterUrl = twitter !=null ? twitter.Url.Replace("https://x.com/","").Replace("https://twitter.com/","").Replace("https://www.twitter.com/","") : ""; var twitterUrl = twitter !=null ? twitter.Url.Replace("https://x.com/","").Replace("https://twitter.com/","").Replace("https://www.twitter.com/","") : "";
var whatsappUrl = whatsapp !=null ? whatsapp.Url.Replace("https://wa.me/","").Replace("whatsapp://","") : ""; var whatsappUrl = whatsapp !=null ? whatsapp.Url.Replace("https://wa.me/","").Replace("whatsapp://","") : "";
var instagramUrl = instagram !=null ? instagram.Url.Replace("https://instagram.com/","").Replace("https://www.instagram.com/","") : ""; var instagramUrl = instagram !=null ? instagram.Url.Replace("https://instagram.com/","").Replace("https://www.instagram.com/","") : "";
var tiktokUrl = tiktok !=null ? tiktok.Url.Replace("https://tiktok.com/@@","").Replace("https://www.tiktok.com/@@","").Replace("https://vm.tiktok.com/","") : ""; var tiktokUrl = tiktok !=null ? tiktok.Url.Replace("https://tiktok.com/@","").Replace("https://www.tiktok.com/@","").Replace("https://vm.tiktok.com/","") : "";
var pinterestUrl = pinterest !=null ? pinterest.Url.Replace("https://pinterest.com/","").Replace("https://www.pinterest.com/","").Replace("https://pin.it/","") : ""; var pinterestUrl = pinterest !=null ? pinterest.Url.Replace("https://pinterest.com/","").Replace("https://www.pinterest.com/","").Replace("https://pin.it/","") : "";
var discordUrl = discord !=null ? discord.Url.Replace("https://discord.gg/","").Replace("https://discord.com/invite/","") : ""; var discordUrl = discord !=null ? discord.Url.Replace("https://discord.gg/","").Replace("https://discord.com/invite/","") : "";
var kawaiUrl = kawai !=null ? kawai.Url.Replace("https://kawai.com/","").Replace("https://www.kawai.com/","") : ""; var kawaiUrl = kawai !=null ? kawai.Url.Replace("https://kawai.com/","").Replace("https://www.kawai.com/","") : "";
@ -573,9 +573,12 @@
<i class="fab fa-whatsapp text-success me-2"></i> <i class="fab fa-whatsapp text-success me-2"></i>
https://wa.me/ https://wa.me/
</span> </span>
<input type="text" class="form-control" id="whatsappNumber" placeholder="5511999999999"> <input type="text" class="form-control" id="whatsappNumber" placeholder="11987654321">
</div> </div>
<small class="form-text text-muted">Exemplo: 5511999999999 (código do país + DDD + número)</small> <small class="form-text text-muted">
<strong>Brasil:</strong> Digite apenas DDD + número (ex: 11987654321). O código 55 será adicionado automaticamente.<br>
<strong>Outros países:</strong> Digite o código do país + número completo.
</small>
<input asp-for="WhatsAppNumber" type="hidden" value="@(whatsappUrl ?? "")"> <input asp-for="WhatsAppNumber" type="hidden" value="@(whatsappUrl ?? "")">
<span asp-validation-for="WhatsAppNumber" class="text-danger"></span> <span asp-validation-for="WhatsAppNumber" class="text-danger"></span>
</div> </div>
@ -1204,14 +1207,22 @@
checkServerImageErrors(); checkServerImageErrors();
// Garantir que campos não marcados sejam string vazia ao submeter // Garantir que campos não marcados sejam string vazia ao submeter
$('form').on('submit', function() { $('form').on('submit', function(e) {
ensureUncheckedFieldsAreEmpty(); ensureUncheckedFieldsAreEmpty();
// Validar URLs de redes sociais antes de submeter
const validationResult = validateSocialMediaUrls();
if (!validationResult.valid) {
e.preventDefault();
alert('Erro de validação:\n\n' + validationResult.errors.join('\n'));
return false;
}
// Debug: Verificar quais campos de links estão sendo enviados // Debug: Verificar quais campos de links estão sendo enviados
console.log('=== DEBUG FORM SUBMISSION ==='); console.log('=== DEBUG FORM SUBMISSION ===');
const formData = new FormData(this); const formData = new FormData(this);
for (let [key, value] of formData.entries()) { for (let [key, value] of formData.entries()) {
if (key.includes('Links[')) { if (key.includes('Links[') || key.includes('Url') || key.includes('Number')) {
console.log(`${key}: ${value}`); console.log(`${key}: ${value}`);
} }
} }
@ -2022,6 +2033,82 @@
}); });
} }
// Validar URLs de redes sociais antes de submeter o formulário
function validateSocialMediaUrls() {
const errors = [];
const userLang = navigator.language || navigator.userLanguage;
const isBrazil = userLang.startsWith('pt-BR') || userLang.startsWith('pt');
// Validar WhatsApp
if ($('#enableWhatsApp').is(':checked')) {
const whatsappValue = $('input[name="WhatsAppNumber"]').val().trim();
if (whatsappValue && whatsappValue !== ' ') {
const cleanNumber = whatsappValue.replace('https://wa.me/', '').replace(/\D/g, '');
if (isBrazil) {
// Brasil: deve ter 13 dígitos (55 + DDD + número)
if (cleanNumber.length !== 13 || !cleanNumber.startsWith('55')) {
errors.push('⚠️ WhatsApp: Número brasileiro deve ter 13 dígitos (55 + DDD + número).\nExemplo: 5511987654321');
}
} else {
// Outros países: validar entre 10 e 15 dígitos
if (cleanNumber.length < 10 || cleanNumber.length > 15) {
errors.push('⚠️ WhatsApp: Número deve ter entre 10 e 15 dígitos (incluindo código do país).');
}
}
}
}
// Validar Facebook
if ($('#enableFacebook').is(':checked')) {
const facebookValue = $('input[name="FacebookUrl"]').val().trim();
if (facebookValue && facebookValue !== ' ') {
const cleanUrl = facebookValue.replace('https://facebook.com/', '').replace('https://www.facebook.com/', '');
if (cleanUrl.length < 3) {
errors.push('⚠️ Facebook: Nome de usuário deve ter pelo menos 3 caracteres.');
}
}
}
// Validar Instagram
if ($('#enableInstagram').is(':checked')) {
const instagramValue = $('input[name="InstagramUrl"]').val().trim();
if (instagramValue && instagramValue !== ' ') {
const cleanUrl = instagramValue.replace('https://instagram.com/', '').replace('https://www.instagram.com/', '');
if (cleanUrl.length < 3) {
errors.push('⚠️ Instagram: Nome de usuário deve ter pelo menos 3 caracteres.');
}
}
}
// Validar Twitter
if ($('#enableTwitter').is(':checked')) {
const twitterValue = $('input[name="TwitterUrl"]').val().trim();
if (twitterValue && twitterValue !== ' ') {
const cleanUrl = twitterValue.replace('https://x.com/', '').replace('https://twitter.com/', '');
if (cleanUrl.length < 3) {
errors.push('⚠️ Twitter/X: Nome de usuário deve ter pelo menos 3 caracteres.');
}
}
}
// Validar TikTok
if ($('#enableTiktok').is(':checked')) {
const tiktokValue = $('input[name="TiktokUrl"]').val().trim();
if (tiktokValue && tiktokValue !== ' ') {
const cleanUrl = tiktokValue.replace('https://tiktok.com/@@', '').replace('https://www.tiktok.com/@@', '');
if (cleanUrl.length < 3) {
errors.push('⚠️ TikTok: Nome de usuário deve ter pelo menos 3 caracteres.');
}
}
}
return {
valid: errors.length === 0,
errors: errors
};
}
// Social Media Functions // Social Media Functions
function cleanSocialPrefix(value, socialType) { function cleanSocialPrefix(value, socialType) {
const prefixes = { const prefixes = {
@ -2039,10 +2126,16 @@
for (let prefix of typePrefixes) { for (let prefix of typePrefixes) {
if (value.startsWith(prefix)) { if (value.startsWith(prefix)) {
return value.replace(prefix, ''); value = value.replace(prefix, '');
break;
} }
} }
// TikTok: remover @@ do início (o prefixo já tem @@)
if (socialType === 'Tiktok' && value.startsWith('@@')) {
value = value.substring(1);
}
return value; return value;
} }
@ -2124,6 +2217,16 @@
if (isWhatsApp) { if (isWhatsApp) {
// WhatsApp: apenas números // WhatsApp: apenas números
value = value.replace(/\D/g, ''); value = value.replace(/\D/g, '');
// Auto-adicionar código +55 para números brasileiros (11 dígitos sem código país)
// Detecta cultura pt-BR para aplicar validação BR
const userLang = navigator.language || navigator.userLanguage;
const isBrazil = userLang.startsWith('pt-BR') || userLang.startsWith('pt');
if (isBrazil && value.length === 11 && !value.startsWith('55')) {
value = '55' + value;
}
$(this).val(value); $(this).val(value);
} }
@ -2146,12 +2249,26 @@
if (!value) return; if (!value) return;
if (isWhatsApp) { if (isWhatsApp) {
// Validar WhatsApp (mínimo 10 dígitos) // Validar WhatsApp com detecção de idioma
if (value.length >= 10) { const userLang = navigator.language || navigator.userLanguage;
const isBrazil = userLang.startsWith('pt-BR') || userLang.startsWith('pt');
if (isBrazil) {
// Brasil: validar 13 dígitos (55 + DDD + número)
if (value.length === 13 && value.startsWith('55')) {
input.addClass('is-valid'); input.addClass('is-valid');
} else { } else if (value.length >= 10) {
// Ainda está digitando ou formato incorreto
input.addClass('is-invalid'); input.addClass('is-invalid');
} }
} else {
// Outros países: validar mínimo 10 dígitos (genérico)
if (value.length >= 10 && value.length <= 15) {
input.addClass('is-valid');
} else if (value.length > 0) {
input.addClass('is-invalid');
}
}
} else { } else {
// Validar username (mínimo 3 caracteres, sem espaços) // Validar username (mínimo 3 caracteres, sem espaços)
if (value.length >= 3 && !/\s/.test(value)) { if (value.length >= 3 && !/\s/.test(value)) {

View File

@ -10,6 +10,84 @@
Layout = isPreview ? "_PreviewLayout" : "_UserPageLayout"; Layout = isPreview ? "_PreviewLayout" : "_UserPageLayout";
} }
@functions {
/// <summary>
/// Normaliza URLs de redes sociais para garantir que sempre tenham protocolo HTTPS
/// Corrige URLs que foram salvas sem prefixo HTTP(S)
/// </summary>
string NormalizeSocialUrl(string url, string icon)
{
if (string.IsNullOrEmpty(url)) return "#";
// Se já tem protocolo, retorna direto
if (url.StartsWith("http://") || url.StartsWith("https://"))
return url;
// WhatsApp - garantir prefixo wa.me
if (!string.IsNullOrEmpty(icon) && icon.Contains("whatsapp"))
{
// Remove qualquer prefixo parcial que possa existir
var cleanUrl = url.Replace("wa.me/", "").Replace("whatsapp://", "");
return $"https://wa.me/{cleanUrl}";
}
// Facebook
if (!string.IsNullOrEmpty(icon) && icon.Contains("facebook"))
{
var cleanUrl = url.Replace("facebook.com/", "").Replace("fb.com/", "");
return $"https://facebook.com/{cleanUrl}";
}
// Instagram
if (!string.IsNullOrEmpty(icon) && icon.Contains("instagram"))
{
var cleanUrl = url.Replace("instagram.com/", "").Replace("instagr.am/", "");
return $"https://instagram.com/{cleanUrl}";
}
// Twitter/X
if (!string.IsNullOrEmpty(icon) && (icon.Contains("twitter") || icon.Contains("x-twitter")))
{
var cleanUrl = url.Replace("x.com/", "").Replace("twitter.com/", "");
return $"https://x.com/{cleanUrl}";
}
// TikTok
if (!string.IsNullOrEmpty(icon) && icon.Contains("tiktok"))
{
var cleanUrl = url.Replace("tiktok.com/", "").Replace("tiktok.com/@", "");
// Se não tem @, adiciona
if (!cleanUrl.StartsWith("@"))
cleanUrl = "@" + cleanUrl;
return $"https://tiktok.com/{cleanUrl}";
}
// Pinterest
if (!string.IsNullOrEmpty(icon) && icon.Contains("pinterest"))
{
var cleanUrl = url.Replace("pinterest.com/", "").Replace("pin.it/", "");
return $"https://pinterest.com/{cleanUrl}";
}
// Discord
if (!string.IsNullOrEmpty(icon) && icon.Contains("discord"))
{
var cleanUrl = url.Replace("discord.gg/", "").Replace("discord.com/invite/", "");
return $"https://discord.gg/{cleanUrl}";
}
// Kawai ou qualquer outra rede não identificada
// Se contém domínio, apenas adiciona https://
if (url.Contains(".") || url.Contains("/"))
{
return $"https://{url.TrimStart('/')}";
}
// Fallback: assume que é uma URL completa que está faltando protocolo
return $"https://{url}";
}
}
@section Head { @section Head {
@if (isLivePage) @if (isLivePage)
{ {
@ -233,7 +311,7 @@
(link.Type == BCards.Web.Models.LinkType.Product && !string.IsNullOrEmpty(link.ProductDescription))); (link.Type == BCards.Web.Models.LinkType.Product && !string.IsNullOrEmpty(link.ProductDescription)));
<div class="universal-link" data-link-id="@i"> <div class="universal-link" data-link-id="@i">
<a href="@link.Url" <a href="@NormalizeSocialUrl(link.Url, link.Icon)"
class="universal-link-header" class="universal-link-header"
onclick="recordClick('@Model.Id', @i)" onclick="recordClick('@Model.Id', @i)"
target="_blank" target="_blank"