feat: ajustes na seleção do tipo de qr code.
Some checks failed
Deploy QR Rapido / test (push) Successful in 27s
Deploy QR Rapido / build-and-push (push) Failing after 7s
Deploy QR Rapido / deploy-staging (push) Has been skipped
Deploy QR Rapido / deploy-production (push) Has been skipped

This commit is contained in:
Ricardo Carneiro 2025-08-01 16:35:52 -03:00
parent 286da5e5de
commit 0c176a2abf
10 changed files with 805 additions and 73 deletions

View File

@ -6,7 +6,7 @@
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"applicationUrl": "https://localhost:52428;https://192.168.0.85:52428;http://localhost:52429"
"applicationUrl": "https://localhost:52428;http://192.168.0.85:52429;http://localhost:52429"
}
}
}

View File

@ -393,6 +393,15 @@ namespace QRRapidoApp.Resources {
}
}
/// <summary>
/// Looks up a localized string similar to Sem senha .
/// </summary>
public static string OpenNetwork {
get {
return ResourceManager.GetString("OpenNetwork", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to QR Rapido Premium.
/// </summary>
@ -681,6 +690,15 @@ namespace QRRapidoApp.Resources {
}
}
/// <summary>
/// Looks up a localized string similar to WEP (muito antigo).
/// </summary>
public static string WEPLegacy {
get {
return ResourceManager.GetString("WEPLegacy", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to WiFi.
/// </summary>
@ -689,5 +707,14 @@ namespace QRRapidoApp.Resources {
return ResourceManager.GetString("WiFiType", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Rede WPA (a mais comum).
/// </summary>
public static string WPARecommended {
get {
return ResourceManager.GetString("WPARecommended", resourceCulture);
}
}
}
}

View File

@ -1,5 +1,64 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
@ -28,9 +87,9 @@
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
@ -39,7 +98,7 @@
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
@ -268,7 +327,15 @@
<data name="Colorful" xml:space="preserve">
<value>Colorful</value>
</data>
<!-- Premium Upgrade Page -->
<data name="WPARecommended" xml:space="preserve">
<value>WPA netwrok (most commom)</value>
</data>
<data name="WEPLegacy" xml:space="preserve">
<value>WEP (muito antigo)</value>
</data>
<data name="OpenNetwork" xml:space="preserve">
<value>No password</value>
</data>
<data name="PremiumAccelerateProductivity" xml:space="preserve">
<value>Accelerate your productivity with the world's fastest QR generator</value>
</data>
@ -389,7 +456,6 @@
<data name="PaymentErrorTryAgain" xml:space="preserve">
<value>Payment processing error. Please try again.</value>
</data>
<!-- Ad Space -->
<data name="Advertisement" xml:space="preserve">
<value>Advertisement</value>
</data>
@ -402,7 +468,6 @@
<data name="PremiumBenefitsShort" xml:space="preserve">
<value>Premium: No ads + History + Unlimited QR</value>
</data>
<!-- Login Page -->
<data name="LoginAndGet" xml:space="preserve">
<value>Login with your account and get:</value>
</data>
@ -439,7 +504,6 @@
<data name="PrivacyPolicy" xml:space="preserve">
<value>Privacy Policy</value>
</data>
<!-- Payment Pages -->
<data name="PaymentSuccessful" xml:space="preserve">
<value>Payment successful!</value>
</data>
@ -494,7 +558,6 @@
<data name="PaymentInitializationError" xml:space="preserve">
<value>An error occurred while initializing payment. Please try again.</value>
</data>
<!-- History Page -->
<data name="QRCodesSavedHere" xml:space="preserve">
<value>Your generated QR codes are saved here for future download</value>
</data>
@ -522,7 +585,6 @@
<data name="ErrorRegeneratingQR" xml:space="preserve">
<value>Error regenerating QR Code. Please try again.</value>
</data>
<!-- Home Page -->
<data name="SmallSize200px" xml:space="preserve">
<value>Small (200px)</value>
</data>
@ -598,6 +660,27 @@
<data name="AcceleratePrice" xml:space="preserve">
<value>Accelerate for $19.90/month</value>
</data>
<data name="ChooseTypeFirst" xml:space="preserve">
<value>First, choose the QR Code type</value>
</data>
<data name="TypeGuideURL" xml:space="preserve">
<value>🌐 To generate QR for URL, enter the complete address (ex: https://google.com)</value>
</data>
<data name="TypeGuideVCard" xml:space="preserve">
<value>👤 For business card, fill in name, phone and email in the fields below</value>
</data>
<data name="TypeGuideWiFi" xml:space="preserve">
<value>📶 For WiFi, enter network name, password and security type</value>
</data>
<data name="TypeGuideSMS" xml:space="preserve">
<value>💬 For SMS, enter recipient number and message</value>
</data>
<data name="TypeGuideEmail" xml:space="preserve">
<value>📧 For email, fill in recipient, subject and message (optional)</value>
</data>
<data name="TypeGuideText" xml:space="preserve">
<value>📝 For free text, enter any content you want</value>
</data>
<data name="TipsFasterQR" xml:space="preserve">
<value>Tips for Faster QR</value>
</data>
@ -640,7 +723,6 @@
<data name="ManyAds" xml:space="preserve">
<value>Many ads</value>
</data>
<!-- Layout -->
<data name="Average" xml:space="preserve">
<value>average</value>
</data>

View File

@ -1,5 +1,64 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
@ -28,9 +87,9 @@
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
@ -39,7 +98,7 @@
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
@ -268,7 +327,15 @@
<data name="Colorful" xml:space="preserve">
<value>Colorido</value>
</data>
<!-- Premium Upgrade Page -->
<data name="WPARecommended" xml:space="preserve">
<value>Rede WPA (a mais comum)</value>
</data>
<data name="WEPLegacy" xml:space="preserve">
<value>WEP (muito antigo)</value>
</data>
<data name="OpenNetwork" xml:space="preserve">
<value>Sin contrasenha </value>
</data>
<data name="PremiumAccelerateProductivity" xml:space="preserve">
<value>Acelera tu productividad con el generador de QR más rápido del mundo</value>
</data>
@ -389,7 +456,6 @@
<data name="PaymentErrorTryAgain" xml:space="preserve">
<value>Error al procesar el pago. Inténtalo de nuevo.</value>
</data>
<!-- Ad Space -->
<data name="Advertisement" xml:space="preserve">
<value>Publicidad</value>
</data>
@ -402,7 +468,6 @@
<data name="PremiumBenefitsShort" xml:space="preserve">
<value>Premium: Sin anuncios + Historial + QR ilimitados</value>
</data>
<!-- Login Page -->
<data name="LoginAndGet" xml:space="preserve">
<value>Inicia sesión con tu cuenta y obtén:</value>
</data>
@ -439,7 +504,6 @@
<data name="PrivacyPolicy" xml:space="preserve">
<value>Política de Privacidad</value>
</data>
<!-- Payment Pages -->
<data name="PaymentSuccessful" xml:space="preserve">
<value>¡Pago exitoso!</value>
</data>
@ -494,7 +558,6 @@
<data name="PaymentInitializationError" xml:space="preserve">
<value>Ocurrió un error al iniciar el pago. Inténtalo de nuevo.</value>
</data>
<!-- History Page -->
<data name="QRCodesSavedHere" xml:space="preserve">
<value>Tus códigos QR generados se guardan aquí para descarga futura</value>
</data>
@ -522,7 +585,6 @@
<data name="ErrorRegeneratingQR" xml:space="preserve">
<value>Error al regenerar el Código QR. Inténtalo de nuevo.</value>
</data>
<!-- Home Page -->
<data name="SmallSize200px" xml:space="preserve">
<value>Pequeño (200px)</value>
</data>
@ -598,6 +660,27 @@
<data name="AcceleratePrice" xml:space="preserve">
<value>Acelerar por $19.90/mes</value>
</data>
<data name="ChooseTypeFirst" xml:space="preserve">
<value>Primero, elige el tipo de código QR</value>
</data>
<data name="TypeGuideURL" xml:space="preserve">
<value>🌐 Para generar QR de URL, ingresa la dirección completa (ej: https://google.com)</value>
</data>
<data name="TypeGuideVCard" xml:space="preserve">
<value>👤 Para tarjeta de visita, completa nombre, teléfono y email en los campos</value>
</data>
<data name="TypeGuideWiFi" xml:space="preserve">
<value>📶 Para WiFi, ingresa nombre de red, contraseña y tipo de seguridad</value>
</data>
<data name="TypeGuideSMS" xml:space="preserve">
<value>💬 Para SMS, ingresa el número del destinatario y el mensaje</value>
</data>
<data name="TypeGuideEmail" xml:space="preserve">
<value>📧 Para email, completa destinatario, asunto y mensaje (opcional)</value>
</data>
<data name="TypeGuideText" xml:space="preserve">
<value>📝 Para texto libre, ingresa cualquier contenido que desees</value>
</data>
<data name="TipsFasterQR" xml:space="preserve">
<value>Consejos para QR Más Rápidos</value>
</data>
@ -640,7 +723,6 @@
<data name="ManyAds" xml:space="preserve">
<value>Muchos anuncios</value>
</data>
<!-- Layout -->
<data name="Average" xml:space="preserve">
<value>promedio</value>
</data>

View File

@ -1,5 +1,64 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
@ -28,9 +87,9 @@
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
@ -39,7 +98,7 @@
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
@ -268,7 +327,15 @@
<data name="Colorful" xml:space="preserve">
<value>Colorido</value>
</data>
<!-- Premium Upgrade Page -->
<data name="WPARecommended" xml:space="preserve">
<value>Rede WPA (a mais comum)</value>
</data>
<data name="WEPLegacy" xml:space="preserve">
<value>WEP (muito antigo)</value>
</data>
<data name="OpenNetwork" xml:space="preserve">
<value>Sem senha </value>
</data>
<data name="PremiumAccelerateProductivity" xml:space="preserve">
<value>Acelere sua produtividade com o gerador de QR mais rápido do mundo</value>
</data>
@ -389,7 +456,6 @@
<data name="PaymentErrorTryAgain" xml:space="preserve">
<value>Erro ao processar pagamento. Tente novamente.</value>
</data>
<!-- Ad Space -->
<data name="Advertisement" xml:space="preserve">
<value>Publicidade</value>
</data>
@ -402,7 +468,6 @@
<data name="PremiumBenefitsShort" xml:space="preserve">
<value>Premium: Sem anúncios + Histórico + QR ilimitados</value>
</data>
<!-- Login Page -->
<data name="LoginAndGet" xml:space="preserve">
<value>Entre com sua conta e ganhe:</value>
</data>
@ -439,7 +504,6 @@
<data name="PrivacyPolicy" xml:space="preserve">
<value>Política de Privacidade</value>
</data>
<!-- Payment Pages -->
<data name="PaymentSuccessful" xml:space="preserve">
<value>Pagamento bem-sucedido!</value>
</data>
@ -494,7 +558,6 @@
<data name="PaymentInitializationError" xml:space="preserve">
<value>Ocorreu um erro ao iniciar o pagamento. Tente novamente.</value>
</data>
<!-- History Page -->
<data name="QRCodesSavedHere" xml:space="preserve">
<value>Seus QR codes gerados ficam salvos aqui para download futuro</value>
</data>
@ -522,7 +585,6 @@
<data name="ErrorRegeneratingQR" xml:space="preserve">
<value>Erro ao regenerar QR Code. Tente novamente.</value>
</data>
<!-- Home Page -->
<data name="SmallSize200px" xml:space="preserve">
<value>Pequeno (200px)</value>
</data>
@ -598,6 +660,27 @@
<data name="AcceleratePrice" xml:space="preserve">
<value>Acelerar por R$ 19,90/mês</value>
</data>
<data name="ChooseTypeFirst" xml:space="preserve">
<value>Primeiro, escolha o tipo de QR Code</value>
</data>
<data name="TypeGuideURL" xml:space="preserve">
<value>🌐 Para gerar QR de URL, digite o endereço completo (ex: https://google.com)</value>
</data>
<data name="TypeGuideVCard" xml:space="preserve">
<value>👤 Para cartão de visita, preencha nome, telefone e email nos campos abaixo</value>
</data>
<data name="TypeGuideWiFi" xml:space="preserve">
<value>📶 Para WiFi, informe nome da rede, senha e tipo de segurança</value>
</data>
<data name="TypeGuideSMS" xml:space="preserve">
<value>💬 Para SMS, digite o número do destinatário e a mensagem</value>
</data>
<data name="TypeGuideEmail" xml:space="preserve">
<value>📧 Para email, preencha destinatário, assunto e mensagem (opcional)</value>
</data>
<data name="TypeGuideText" xml:space="preserve">
<value>📝 Para texto livre, digite qualquer conteúdo que desejar</value>
</data>
<data name="TipsFasterQR" xml:space="preserve">
<value>Dicas para QR Mais Rápidos</value>
</data>
@ -640,7 +723,6 @@
<data name="ManyAds" xml:space="preserve">
<value>Muitos anúncios</value>
</data>
<!-- Layout -->
<data name="Average" xml:space="preserve">
<value>médio</value>
</data>

View File

@ -1,5 +1,64 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
@ -28,9 +87,9 @@
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
@ -39,7 +98,7 @@
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
@ -268,4 +327,13 @@
<data name="Colorful" xml:space="preserve">
<value>Colorful</value>
</data>
<data name="WPARecommended" xml:space="preserve">
<value>Rede WPA (a mais comum)</value>
</data>
<data name="WEPLegacy" xml:space="preserve">
<value>WEP (muito antigo)</value>
</data>
<data name="OpenNetwork" xml:space="preserve">
<value>Sem senha </value>
</data>
</root>

View File

@ -75,7 +75,7 @@
<label class="form-label fw-semibold">
<i class="fas fa-list"></i> @Localizer["QRCodeType"]
</label>
<select id="qr-type" class="form-select" required>
<select id="qr-type" class="form-select qr-type-highlight" required>
<option value="">@Localizer["SelectType"]</option>
<option value="url">🌐 @Localizer["URLLink"]</option>
<option value="text">📝 @Localizer["SimpleText"]</option>
@ -84,6 +84,20 @@
<option value="sms">💬 @Localizer["SMS"]</option>
<option value="email">📧 @Localizer["Email"]</option>
</select>
<div class="type-selection-hint" id="type-selection-hint">
<i class="fas fa-hand-point-up"></i>
<span>@Localizer["ChooseTypeFirst"]</span>
</div>
<!-- Hidden localized guidance messages for JavaScript -->
<div style="display: none;">
<span data-type-guide-url>@Localizer["TypeGuideURL"]</span>
<span data-type-guide-vcard>@Localizer["TypeGuideVCard"]</span>
<span data-type-guide-wifi>@Localizer["TypeGuideWiFi"]</span>
<span data-type-guide-sms>@Localizer["TypeGuideSMS"]</span>
<span data-type-guide-email>@Localizer["TypeGuideEmail"]</span>
<span data-type-guide-text>@Localizer["TypeGuideText"]</span>
</div>
</div>
<div class="col-md-6 mb-3">
<label class="form-label fw-semibold">
@ -680,10 +694,10 @@
</button>
</div>
<!-- Share Button with Dropdown -->
<!-- Share Button with Dropdown (Temporarily Disabled) -->
<div class="dropdown w-100 mb-3">
<button class="btn btn-primary dropdown-toggle w-100" type="button" id="share-qr-btn" data-bs-toggle="dropdown" aria-expanded="false">
<i class="fas fa-share-alt"></i> @Localizer["ShareQRCode"]
<button class="btn btn-secondary dropdown-toggle w-100" type="button" id="share-qr-btn" disabled title="Em desenvolvimento">
<i class="fas fa-share-alt"></i> @Localizer["ShareQRCode"] (Em breve)
</button>
<ul class="dropdown-menu w-100" aria-labelledby="share-qr-btn" id="share-dropdown">
<!-- Native share option (mobile only) -->

View File

@ -47,6 +47,14 @@ body {
box-shadow: 0 4px 8px rgba(0, 123, 255, 0.3);
}
/* Force dropdown to appear above all elements */
.dropdown-menu {
z-index: 1060 !important;
position: absolute !important;
transform: none !important;
transition: none !important;
}
#share-dropdown {
min-width: 250px;
border: none;
@ -129,6 +137,99 @@ body {
color: #6f42c1 !important;
}
/* QR Type Field Highlight */
.qr-type-highlight {
border: 2px solid #007bff !important;
box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25) !important;
animation: gentle-pulse 2s infinite;
background-color: rgba(0, 123, 255, 0.05) !important;
}
@keyframes gentle-pulse {
0% {
box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25);
}
50% {
box-shadow: 0 0 0 0.4rem rgba(0, 123, 255, 0.15);
}
100% {
box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25);
}
}
/* Helper text for type selection */
.type-selection-hint {
font-size: 1.75rem; /* Doubled from 0.875rem */
color: #495057;
font-weight: 600;
margin-top: 0.5rem;
display: flex;
align-items: center;
gap: 1rem; /* Increased gap */
justify-content: center;
padding: 1rem;
background: linear-gradient(135deg, rgba(0, 123, 255, 0.1), rgba(0, 123, 255, 0.05));
border-radius: 0.5rem;
border: 2px dashed rgba(0, 123, 255, 0.3);
animation: hint-appear 0.5s ease-out;
}
.type-selection-hint i {
font-size: 2rem; /* Larger icon */
color: #007bff;
animation: point-up 2s infinite;
}
.type-selection-hint span {
animation: text-glow 3s infinite;
}
@keyframes hint-appear {
0% {
opacity: 0;
transform: translateY(-10px) scale(0.95);
}
100% {
opacity: 1;
transform: translateY(0) scale(1);
}
}
@keyframes point-up {
0%, 100% {
transform: translateY(0) scale(1);
}
50% {
transform: translateY(-5px) scale(1.1);
color: #0056b3;
}
}
@keyframes text-glow {
0%, 100% {
text-shadow: none;
}
50% {
text-shadow: 0 0 8px rgba(0, 123, 255, 0.3);
}
}
/* Fade out animation */
.type-selection-hint.fade-out {
animation: hint-fadeout 1s ease-out forwards;
}
@keyframes hint-fadeout {
0% {
opacity: 1;
transform: scale(1);
}
100% {
opacity: 0;
transform: scale(0.95);
}
}
/* Share success feedback */
.share-success {
position: fixed;

View File

@ -703,6 +703,7 @@ class QRRapidoGenerator {
// Save current data
this.currentQR = {
base64: result.qrCodeBase64,
qrCodeBase64: result.qrCodeBase64, // Both properties for compatibility
id: result.qrId,
generationTime: generationTime
};
@ -1040,28 +1041,159 @@ class QRRapidoGenerator {
}
async downloadQR(format) {
if (!this.currentQR) return;
if (!this.currentQR || !this.currentQR.qrCodeBase64) {
this.showError('Nenhum QR Code gerado para download.');
return;
}
try {
const response = await fetch(`/api/QR/Download/${this.currentQR.id}?format=${format}`);
if (!response.ok) throw new Error('Download failed');
const timestamp = new Date().toISOString().slice(0,10);
const base64Data = this.currentQR.qrCodeBase64;
const blob = await response.blob();
const url = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = `qrrapido-${new Date().toISOString().slice(0,10)}.${format}`;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
if (format === 'png') {
// Download PNG directly from base64
const link = document.createElement('a');
link.href = `data:image/png;base64,${base64Data}`;
link.download = `qrrapido-${timestamp}.png`;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
} else if (format === 'svg') {
// Convert PNG to SVG
const svgData = await this.convertPngToSvg(base64Data);
const link = document.createElement('a');
link.href = `data:image/svg+xml;charset=utf-8,${encodeURIComponent(svgData)}`;
link.download = `qrrapido-${timestamp}.svg`;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
} else if (format === 'pdf') {
// Convert PNG to PDF
const pdfBlob = await this.convertPngToPdf(base64Data);
const url = window.URL.createObjectURL(pdfBlob);
const link = document.createElement('a');
link.href = url;
link.download = `qrrapido-${timestamp}.pdf`;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
window.URL.revokeObjectURL(url);
}
} catch (error) {
console.error('Download error:', error);
this.showError('Erro ao fazer download. Tente novamente.');
this.showError(`Erro ao fazer download ${format.toUpperCase()}. Tente novamente.`);
}
}
async convertPngToSvg(base64Data) {
return new Promise((resolve) => {
const img = new Image();
img.onload = () => {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
canvas.width = img.width;
canvas.height = img.height;
ctx.drawImage(img, 0, 0);
// Create SVG with embedded base64 image
const svgData = `<?xml version="1.0" encoding="UTF-8"?>
<svg width="${img.width}" height="${img.height}" xmlns="http://www.w3.org/2000/svg">
<image width="${img.width}" height="${img.height}" href="data:image/png;base64,${base64Data}"/>
</svg>`;
resolve(svgData);
};
img.src = `data:image/png;base64,${base64Data}`;
});
}
async convertPngToPdf(base64Data) {
return new Promise((resolve, reject) => {
try {
// Try to use jsPDF if available, otherwise use a simpler approach
if (typeof window.jsPDF !== 'undefined') {
const pdf = new window.jsPDF();
const img = new Image();
img.onload = () => {
// Calculate dimensions to fit on page
const pdfWidth = pdf.internal.pageSize.getWidth();
const pdfHeight = pdf.internal.pageSize.getHeight();
const imgRatio = img.width / img.height;
let width = Math.min(pdfWidth - 20, 150);
let height = width / imgRatio;
if (height > pdfHeight - 20) {
height = pdfHeight - 20;
width = height * imgRatio;
}
// Center on page
const x = (pdfWidth - width) / 2;
const y = (pdfHeight - height) / 2;
pdf.addImage(`data:image/png;base64,${base64Data}`, 'PNG', x, y, width, height);
const pdfBlob = pdf.output('blob');
resolve(pdfBlob);
};
img.onerror = () => reject(new Error('Failed to load image'));
img.src = `data:image/png;base64,${base64Data}`;
} else {
// Fallback: create a very simple PDF
this.createBasicPdf(base64Data).then(resolve).catch(reject);
}
} catch (error) {
reject(error);
}
});
}
async createBasicPdf(base64Data) {
// Load jsPDF dynamically if not available
if (typeof window.jsPDF === 'undefined') {
await this.loadJsPDF();
}
return new Promise((resolve) => {
const pdf = new window.jsPDF();
const img = new Image();
img.onload = () => {
// Add QR code to PDF
const pdfWidth = pdf.internal.pageSize.getWidth();
const size = Math.min(pdfWidth - 40, 100);
const x = (pdfWidth - size) / 2;
const y = 50;
pdf.addImage(`data:image/png;base64,${base64Data}`, 'PNG', x, y, size, size);
pdf.text('QR Code - QR Rapido', pdfWidth / 2, 30, { align: 'center' });
const pdfBlob = pdf.output('blob');
resolve(pdfBlob);
};
img.src = `data:image/png;base64,${base64Data}`;
});
}
async loadJsPDF() {
return new Promise((resolve, reject) => {
if (typeof window.jsPDF !== 'undefined') {
resolve();
return;
}
const script = document.createElement('script');
script.src = 'https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js';
script.onload = () => {
window.jsPDF = window.jspdf.jsPDF;
resolve();
};
script.onerror = () => reject(new Error('Failed to load jsPDF'));
document.head.appendChild(script);
});
}
async saveToHistory() {
if (!this.currentQR) return;
@ -1221,25 +1353,45 @@ class QRRapidoGenerator {
}
showAlert(message, type) {
const alert = document.createElement('div');
alert.className = `alert alert-${type} alert-dismissible fade show`;
alert.innerHTML = `
<div>${message}</div>
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
// Create toast container if doesn't exist
let toastContainer = document.getElementById('toast-container');
if (!toastContainer) {
toastContainer = document.createElement('div');
toastContainer.id = 'toast-container';
toastContainer.className = 'toast-container position-fixed top-0 end-0 p-3';
toastContainer.style.zIndex = '1060';
document.body.appendChild(toastContainer);
}
// Create toast element
const toast = document.createElement('div');
toast.className = `toast align-items-center text-bg-${type} border-0`;
toast.setAttribute('role', 'alert');
toast.innerHTML = `
<div class="d-flex">
<div class="toast-body">
<i class="fas fa-${type === 'danger' ? 'exclamation-triangle' : 'check-circle'}"></i>
${message}
</div>
<button type="button" class="btn-close btn-close-white me-2 m-auto" data-bs-dismiss="toast"></button>
</div>
`;
const container = document.querySelector('.container');
const row = container?.querySelector('.row');
if (container && row) {
container.insertBefore(alert, row);
}
toastContainer.appendChild(toast);
// Auto-remove after delay
setTimeout(() => {
if (alert.parentNode) {
alert.parentNode.removeChild(alert);
// Show toast
const bsToast = new bootstrap.Toast(toast, {
delay: type === 'success' ? 3000 : 5000,
autohide: true
});
bsToast.show();
// Remove from DOM after hidden
toast.addEventListener('hidden.bs.toast', () => {
if (toast.parentNode) {
toast.parentNode.removeChild(toast);
}
}, type === 'success' ? 3000 : 5000);
});
}
setupRealTimePreview() {
@ -1284,6 +1436,27 @@ class QRRapidoGenerator {
// Setup URL validation event listeners
this.setupURLValidationListeners();
// Setup hint auto-hide timer
this.setupHintAutoHide();
}
setupHintAutoHide() {
const hint = document.getElementById('type-selection-hint');
if (hint) {
// Auto-hide after 30 seconds
setTimeout(() => {
if (hint && !hint.classList.contains('fade-out')) {
hint.classList.add('fade-out');
// Remove from DOM after animation
setTimeout(() => {
if (hint.parentNode) {
hint.style.display = 'none';
}
}, 1000); // 1s for fade-out animation
}
}, 30000); // 30 seconds
}
}
setupURLValidationListeners() {
@ -1329,6 +1502,8 @@ class QRRapidoGenerator {
this.removeInitialHighlight();
// Sempre habilitar campos de conteúdo após selecionar tipo
this.enableContentFields(type);
// Show guidance toast for the selected type
this.showTypeGuidanceToast(type);
} else {
this.disableAllFields();
}
@ -1638,9 +1813,110 @@ class QRRapidoGenerator {
// Remove destaque inicial quando tipo for selecionado
removeInitialHighlight() {
const typeField = document.getElementById('qr-type');
if (typeField && typeField.classList.contains('qr-field-highlight')) {
typeField.classList.remove('qr-field-highlight');
const hint = document.getElementById('type-selection-hint');
if (typeField && typeField.classList.contains('qr-type-highlight')) {
typeField.classList.remove('qr-type-highlight');
}
if (hint && !hint.classList.contains('fade-out')) {
hint.classList.add('fade-out');
// Hide after animation
setTimeout(() => {
if (hint.parentNode) {
hint.style.display = 'none';
}
}, 1000);
}
}
showTypeGuidanceToast(type) {
// Get localized message based on QR type
const messages = this.getTypeGuidanceMessages();
const message = messages[type];
if (message) {
// Create info toast with 30 second duration
const toast = this.createGuidanceToast(message);
this.showGuidanceToast(toast, 30000); // 30 seconds
}
}
getTypeGuidanceMessages() {
// These should match the resource file keys
// In a real implementation, these would come from server-side localization
// For now, we'll use the JavaScript language strings or fallback to Portuguese
return {
'url': document.querySelector('[data-type-guide-url]')?.textContent || '🌐 Para gerar QR de URL, digite o endereço completo (ex: https://google.com)',
'vcard': document.querySelector('[data-type-guide-vcard]')?.textContent || '👤 Para cartão de visita, preencha nome, telefone e email nos campos abaixo',
'wifi': document.querySelector('[data-type-guide-wifi]')?.textContent || '📶 Para WiFi, informe nome da rede, senha e tipo de segurança',
'sms': document.querySelector('[data-type-guide-sms]')?.textContent || '💬 Para SMS, digite o número do destinatário e a mensagem',
'email': document.querySelector('[data-type-guide-email]')?.textContent || '📧 Para email, preencha destinatário, assunto e mensagem (opcional)',
'text': document.querySelector('[data-type-guide-text]')?.textContent || '📝 Para texto livre, digite qualquer conteúdo que desejar'
};
}
createGuidanceToast(message) {
const toast = document.createElement('div');
toast.className = 'toast align-items-center text-bg-info border-0';
toast.setAttribute('role', 'alert');
toast.style.minWidth = '400px';
toast.innerHTML = `
<div class="d-flex">
<div class="toast-body fw-medium">
${message}
</div>
<button type="button" class="btn-close btn-close-white me-2 m-auto" data-bs-dismiss="toast"></button>
</div>
`;
return toast;
}
showGuidanceToast(toast, duration = 30000) {
// Create toast container if doesn't exist (positioned below header stats)
let toastContainer = document.getElementById('guidance-toast-container');
if (!toastContainer) {
toastContainer = document.createElement('div');
toastContainer.id = 'guidance-toast-container';
toastContainer.className = 'toast-container position-relative w-100 d-flex justify-content-center mb-3';
toastContainer.style.zIndex = '1060';
// Try to find existing toast container first (if manually positioned)
const existingContainer = document.getElementById('guidance-toast-container');
if (existingContainer) {
// Use existing container that was manually positioned
toastContainer = existingContainer;
} else {
// Find the main content area and insert after hero section
const mainElement = document.querySelector('main[role="main"]');
if (mainElement) {
// Insert at the beginning of main content
mainElement.insertBefore(toastContainer, mainElement.firstChild);
} else {
// Fallback: find container and insert at top
const container = document.querySelector('.container');
if (container) {
container.insertBefore(toastContainer, container.firstChild);
}
}
}
}
toastContainer.appendChild(toast);
// Show toast with custom duration
const bsToast = new bootstrap.Toast(toast, {
delay: duration,
autohide: true
});
bsToast.show();
// Remove from DOM after hidden
toast.addEventListener('hidden.bs.toast', () => {
if (toast.parentNode) {
toast.parentNode.removeChild(toast);
}
});
}
// ============================================