Saltar al contenido principal

📧 Sistema de Invitaciones por Email - Sin Envío Real

Fecha: 1 de enero, 2025
Estado: ✅ IMPLEMENTADO Y FUNCIONANDO


🎯 Cómo Funciona

Flujo Completo:

  1. Participante crea equipo (/hackathons/[slug]/teams/create)

    • Ingresa nombre del equipo
    • (Opcional) Ingresa emails de compañeros en el campo "Invitar Participantes por Email"
    • Puede agregar hasta 10 emails
  2. Validación en el Cliente (Formulario)

    • ✅ Valida formato de email (HTML5 type="email")
    • ✅ Filtra emails vacíos antes de enviar
  3. Validación en el Servidor (Zod)

    • ✅ Valida formato de email con .email('Email inválido')
    • ✅ Valida que no sean más de 10 emails
  4. Validación de Existencia y Disponibilidad

    • ✅ Verifica que cada email exista en la DB (tabla Profile)
    • ✅ Verifica que el usuario esté registrado en el hackathon
    • ✅ Verifica que no tenga equipo en ese hackathon
    • ✅ Verifica que no haya invitación pendiente ya
  5. Creación de Notificación en DB

    • ✅ Crea registro en TeamInvitation con:
      • email: Email del invitado
      • teamId: ID del equipo
      • senderId: ID del que envía
      • receiverId: ID del invitado (si existe en DB)
      • status: PENDING
  6. Consulta de Notificaciones

    • ✅ Se puede consultar por email: getPendingInvitationsByEmail(email)
    • ✅ Se puede consultar por profileId: getPendingInvitationsByProfileId(profileId)
    • ✅ Se muestra en /notifications cuando el usuario inicia sesión

✅ Lo que SÍ hace:

  • ✅ Valida formato de email (Zod)
  • ✅ Verifica existencia en DB (Profile)
  • ✅ Verifica registro en hackathon
  • ✅ Verifica disponibilidad (sin equipo)
  • ✅ Crea notificación en DB (TeamInvitation)
  • ✅ Consulta por email en DB
  • ✅ Muestra en página de notificaciones

❌ Lo que NO hace:

  • NO envía emails reales (no SMTP, no IMAP, no servicios de email)
  • NO usa servicios externos de notificaciones por email
  • NO requiere configuración de servidor de email

📋 Código Relevante

1. Formulario (create-team-form.tsx)

// Estado para emails
const [inviteEmails, setInviteEmails] = useState<string[]>(['']);

// Al enviar, filtra emails vacíos
const validEmails = inviteEmails.filter(email => email.trim() !== '');

const data: CreateTeamInput = {
name: formData.get('name') as string,
description: (formData.get('description') as string) || undefined,
inviteEmails: validEmails.length > 0 ? validEmails : undefined,
};

2. Validación (Zod) (validations.ts)

export const createTeamSchema = z.object({
name: z.string().min(3).max(100),
description: z.string().max(500).optional(),
inviteEmails: z
.array(z.string().email('Email inválido')) // ✅ Valida formato
.max(10, 'No puedes invitar más de 10 participantes')
.optional(),
});

3. Validación de Existencia (invitations/actions.ts)

// 12. Clasificar emails: existentes vs no existentes en BD
const existingProfiles = await db.profile.findMany({
where: {
email: { in: normalizedEmails }, // ✅ Busca en DB
},
});

// 13. Validar registro en hackathon
const participations = await db.hackathonParticipation.findMany({
where: {
hackathonId: team.hackathonId,
profileId: { in: existingProfileIds },
},
});

// 14. Validar que no tengan equipo
const hasTeamChecks = await Promise.all(
registeredProfileIdsList.map(profileId =>
hasTeamInHackathon(profileId, team.hackathonId)
)
);

4. Creación de Notificación (invitations/actions.ts)

// Crear invitación en DB
const invitation = await db.teamInvitation.create({
data: {
teamId: team.id,
email, // ✅ Email del invitado
senderId: currentUserProfile.id,
receiverId: profile.id, // ✅ ID del perfil (si existe)
status: TeamInvitationStatus.PENDING,
},
});

5. Consulta por Email (invitations/queries.ts)

export async function getPendingInvitationsByEmail(
email: string
): Promise<TeamInvitationWithRelations[]> {
return db.teamInvitation.findMany({
where: {
email, // ✅ Consulta por email
status: TeamInvitationStatus.PENDING,
},
include: {
team: { /* ... */ },
sender: { /* ... */ },
receiver: { /* ... */ },
},
});
}

🧪 Cómo Probar

1. Crear Equipo con Invitaciones

  1. Inicia sesión como PARTICIPANT
  2. Ve a un hackathon en estado REGISTRATION o RUNNING
  3. Haz clic en "Crear Equipo"
  4. Ingresa nombre del equipo
  5. En "Invitar Participantes por Email", agrega emails:
    • test.participant2@example.com
    • test.participant3@example.com
  6. Haz clic en "Crear Equipo"

2. Verificar Notificaciones

  1. Inicia sesión como test.participant2@example.com
  2. Ve a /notifications
  3. Deberías ver la invitación pendiente
  4. Puedes aceptar o rechazar

3. Verificar en DB

-- Ver invitaciones pendientes
SELECT * FROM "TeamInvitation"
WHERE status = 'PENDING';

-- Ver invitaciones por email
SELECT * FROM "TeamInvitation"
WHERE email = 'test.participant2@example.com'
AND status = 'PENDING';

📊 Resultado de la Invitación

Cuando se invita por email, el sistema retorna:

{
success: true,
data: {
created: 2, // Invitaciones creadas exitosamente
skipped: 0, // Emails omitidos (ya tenían invitación pendiente)
errors: [ // Emails con problemas
{ email: 'noexiste@example.com', reason: 'No existe en la plataforma' },
{ email: 'notregistered@example.com', reason: 'No está registrado en este hackathon' },
{ email: 'hasteam@example.com', reason: 'Ya pertenece a un equipo' },
]
}
}

✅ Confirmación

El sistema funciona EXACTAMENTE como lo solicitaste:

  • ✅ Solo valida formato y existencia en DB
  • ✅ Crea notificaciones en DB asociadas al email
  • ✅ Se puede consultar por email
  • NO envía emails reales
  • NO usa SMTP, IMAP ni servicios externos

Última Actualización: 1 de enero, 2025
Estado: ✅ FUNCIONANDO CORRECTAMENTE