Switch to English
KiraWebs

KiraWebs

Proyecto de desarrollo web que presenta una landing page moderna para un servicio de creación de sitios web, enfocada en rendimiento, transparencia y una experiencia clara para negocios y profesionales.

Next.jsNotion APIRedis

Kirawebs.com es un sitio web desarrollado para una empresa que ofrece soluciones tecnológicas, incluyendo desarrollo web, consultoría y soluciones en la nube. 💡 Una de las características más destacadas de este proyecto es la implementación de un formulario interactivo de múltiples pasos que permite a los clientes simular el costo de su sitio web.


Tecnologías Utilizadas

Las siguientes tecnologías fueron utilizadas para el desarrollo de este proyecto:

  • Next.js: Como framework principal para la construcción de la aplicación.
    • React: Para la creación de componentes interactivos.
      • Tailwind CSS: Para el diseño y estilizado de la interfaz.
        • Shadcn: Para la implementación de componentes UI modernos y accesibles.
          • Notion API: Para almacenar los datos del formulario de contacto en una base de datos de Notion.
            • Redis: Para implementar un sistema de rate limiting, limitando el envío de mensajes a 3 por minuto.
              • Zod: Para la validación de datos en el formulario de contacto.
                • ESLint y Prettier: Para mantener un código limpio y bien formateado.
                  • Vercel: Para el despliegue y hosting de la aplicación.
                    Image

                    Características Principales

                    Formulario Interactivo de Simulación de Costos

                    • Los clientes pueden seguir una serie de pasos para obtener una estimación del costo de su sitio web.
                      Image
                      Image
                      Image
                      Image

                      Formulario de Contacto con Validación y Rate Limiting

                      • Se solicita un correo electrónico y un mensaje.
                        • Los datos se validan utilizando Zod.
                          • Si la validación es exitosa, los datos se almacenan en una base de datos de Notion.
                            • Se implementó un sistema de rate limiting usando Redis, limitando el envío de mensajes a 3 por minuto.
                              Image
                              Image
                              Image
                              Image

                              En la Base de Datos de Notion

                              Image

                              Fragmentos de Código y Ejemplos

                              Ejemplo de Validación con Zod

                              const FormSchema = z.object({
                              	email: z.string().email("Email inválido"),
                              	description: z
                              		.string()
                              		.min(5, "La descripción debe tener al menos 5 caracteres")
                              		.max(1500, "La descripcion debe tener menos de 1000 caracteres"),
                              })
                              
                              export async function sendEmail(prevState: unknown, formData: FormData) {
                              	const clientIp = formData.get("clientIp") as string
                              	const result = await ratelimit.limit(clientIp)
                              
                              	if (!result.success) {
                              		return {
                              			success: false,
                              			title: "Limite de envios alcanzado",
                              			details: "Por favor, espera un momento antes de intentar nuevamente.",
                              		}
                              	}
                              	const rawData = {
                              		email: formData.get("email"),
                              		description: formData.get("description"),
                              	}
                              	const validationResult = FormSchema.safeParse(rawData)
                              
                              	if (!validationResult.success) {
                              		return {
                              			success: false
                              			title: "Datos inválidos",
                              			details:
                              				"Por favor, verifica que tu email sea valido y que la descripcion tenga entre 5 y 1500 caracteres.",
                              		}
                              	}
                              
                              	const { email, description } = validationResult.data
                              

                              Almacenamiento del Mensaje de Contacto

                               try {
                                  const response = await notion.pages.create({
                                    parent: { database_id: DATABASE_IDS.contact! },
                                    properties: {
                                      email: {
                                        title: [{ text: { content: email } }],
                                      },
                                      description: {
                                        rich_text: [{ text: { content: description } }],
                                      },
                                    },
                                  })
                              
                                  if (!response) {
                                    return {
                                      success: false,
                                      title: "Error al enviar los datos",
                                      details:
                                        "No pudimos enviar tus datos en este momento. Por favor, intenta nuevamente más tarde o contáctanos por otro medio.",
                                    }
                                  }
                              
                                  return {
                                    success: true,
                                    title: "¡Mensaje enviado!",
                                    details:
                                      "Gracias por contactarnos. Nos pondremos en contacto contigo pronto.",
                                  }
                                } catch {
                                  return {
                                    success: false,
                                    title: "Error al enviar los datos",
                                    details:
                                      "Hubo un problema al procesar tu solicitud. Por favor, intenta más tarde o contáctanos directamente.",
                                  }
                                }
                              }
                              

                              Implementación de Rate Limiting con Redis

                              const ratelimit = new Ratelimit({
                                redis: redis,
                                limiter: Ratelimit.fixedWindow(3, "60 s"),
                              })
                              
                              export async function sendEmail(prevState: unknown, formData: FormData) {
                                const clientIp = formData.get("clientIp") as string
                                const result = await ratelimit.limit(clientIp)
                              
                                if (!result.success) {
                                  return {
                                    success: false,
                                    title: "Límite de envíos alcanzado",
                                    details: "Por favor, espera un momento antes de intentar nuevamente.",
                                  }
                                }
                              

                              Despliegue en Vercel

                              La aplicación está alojada en Vercel, garantizando un rendimiento óptimo y una escalabilidad sencilla.