KiraWebs

KiraWebs

Next.jsNotion APIRedis

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.


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.