feat: invitar a un proyecto por correo a alguien que aun no es usuario

Al agregar un miembro, el popup ahora permite elegir un usuario existente
o escribir un correo nuevo. Si ese correo no pertenece a nadie, se crea
una cuenta en estado 'invited' (nombre/clave placeholder) y se le manda
un correo con un enlace de un solo uso para que complete su registro
(elige su nombre y clave). La fila de project_members se crea de una vez
al invitar, asi que en cuanto acepta ya es miembro sin pasos adicionales.
A diferencia del autorregistro publico, no pasa por aprobacion de un
admin -- quien lo agrego a su proyecto ya lo vetó.

Nuevos endpoints publicos GET /api/auth/invite y POST /api/auth/accept-invite.
Nueva pagina AcceptInvitePage (/aceptar-invitacion). Migracion 004 agrega
el status 'invited' al enum de users.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Milton Gonzalez
2026-09-09 23:55:02 -05:00
parent 53c7bd7f95
commit d7478d0f21
20 changed files with 320 additions and 23 deletions
+16
View File
@@ -25,6 +25,10 @@ export const messages = {
verify_email_title: 'Verificar correo',
verify_email_success: 'Correo confirmado. Tu cuenta esta pendiente de aprobacion por un administrador.',
verify_email_missing_token: 'Enlace invalido: falta el token de verificacion.',
accept_invite_title: 'Completar registro',
accept_invite_hint: 'Te invitaron a un proyecto. Elige tu nombre y una clave para crear tu cuenta.',
accept_invite_submit: 'Crear mi cuenta',
accept_invite_missing_token: 'Enlace invalido: falta el token de invitacion.',
forgot_password_title: 'Recuperar clave',
forgot_password_submit: 'Enviar instrucciones',
forgot_password_sent: 'Si el correo existe, te enviamos instrucciones para recuperar tu clave.',
@@ -72,6 +76,7 @@ export const messages = {
common_view: 'Ver',
common_yes: 'Si',
common_no: 'No',
common_or: 'o',
common_loading: 'Cargando...',
common_empty: 'No hay datos para mostrar',
common_confirm_delete_title: 'Confirmar eliminacion',
@@ -127,6 +132,9 @@ export const messages = {
project_role_admin: 'Administrador',
project_make_admin: 'Hacer administrador',
project_remove_admin: 'Quitar administrador',
project_invite_email: 'Invitar por correo',
project_invite_email_hint: 'Si el correo no pertenece a nadie todavia, se le manda un enlace para que complete su registro y se una al proyecto.',
project_invite_sent: 'Invitacion enviada por correo',
project_created_at: 'Creado',
project_deleted_at: 'Eliminado',
project_delete_confirm_msg: '¿Seguro que deseas eliminar este proyecto? Se movera a la papelera y dejara de verse en el listado; un superadministrador puede restaurarlo o borrarlo definitivamente.',
@@ -225,6 +233,10 @@ export const messages = {
verify_email_title: 'Verify email',
verify_email_success: 'Email confirmed. Your account is pending administrator approval.',
verify_email_missing_token: 'Invalid link: missing verification token.',
accept_invite_title: 'Complete your registration',
accept_invite_hint: 'You were invited to a project. Choose your name and a password to create your account.',
accept_invite_submit: 'Create my account',
accept_invite_missing_token: 'Invalid link: missing invite token.',
forgot_password_title: 'Recover password',
forgot_password_submit: 'Send instructions',
forgot_password_sent: 'If the email exists, we sent instructions to recover your password.',
@@ -272,6 +284,7 @@ export const messages = {
common_view: 'View',
common_yes: 'Yes',
common_no: 'No',
common_or: 'or',
common_loading: 'Loading...',
common_empty: 'No data to show',
common_confirm_delete_title: 'Confirm delete',
@@ -327,6 +340,9 @@ export const messages = {
project_role_admin: 'Administrator',
project_make_admin: 'Make administrator',
project_remove_admin: 'Remove administrator',
project_invite_email: 'Invite by email',
project_invite_email_hint: "If the email doesn't belong to anyone yet, they get a link to finish signing up and join the project.",
project_invite_sent: 'Invite sent by email',
project_created_at: 'Created',
project_deleted_at: 'Deleted',
project_delete_confirm_msg: 'Are you sure you want to delete this project? It will move to the trash and disappear from the list; a superadmin can restore it or delete it permanently.',