feat: add complex user invitation and management features

- Implemented complex member and invitation schemas using Zod for validation.
- Created new API endpoints for inviting, accepting, revoking, and canceling complex user invitations.
- Developed handlers for managing complex users, including listing, inviting, revoking, and canceling invitations.
- Added frontend components for displaying and managing complex users and invitations.
- Introduced session storage management for pending invitations.
- Integrated Gravatar for user avatars based on email.
- Created database migration for complex invitations table.
This commit is contained in:
Jose Selesan
2026-04-22 15:30:04 -03:00
parent 5f2a7fd494
commit 6ad4fc9ee9
37 changed files with 1935 additions and 78 deletions

View File

@@ -117,6 +117,59 @@ export const complexUserRoleSchema = z.enum(['ADMIN', 'EMPLOYEE'])
export type ComplexUserRole = z.infer<typeof complexUserRoleSchema>
export const complexMemberSchema = z.object({
userId: z.string(),
fullName: z.string(),
email: z.email(),
avatarUrl: z.url(),
role: complexUserRoleSchema,
createdAt: z.string().datetime(),
})
export type ComplexMember = z.infer<typeof complexMemberSchema>
export const complexInvitationStatusSchema = z.enum(['PENDING', 'ACCEPTED', 'REVOKED', 'EXPIRED'])
export type ComplexInvitationStatus = z.infer<typeof complexInvitationStatusSchema>
export const complexInvitationSchema = z.object({
id: z.uuid(),
complexId: z.uuid(),
email: z.email(),
status: complexInvitationStatusSchema,
createdAt: z.string().datetime(),
expiresAt: z.string().datetime(),
acceptedAt: z.string().datetime().nullable(),
revokedAt: z.string().datetime().nullable(),
})
export type ComplexInvitation = z.infer<typeof complexInvitationSchema>
export const complexUsersOverviewSchema = z.object({
members: z.array(complexMemberSchema),
invitations: z.array(complexInvitationSchema),
})
export type ComplexUsersOverview = z.infer<typeof complexUsersOverviewSchema>
export const inviteComplexUserSchema = z.object({
email: z.email('El email debe ser válido.'),
})
export type InviteComplexUserInput = z.infer<typeof inviteComplexUserSchema>
export const acceptComplexInvitationsSchema = z.object({
inviteToken: z.string().min(1, 'El token de invitación no puede estar vacío.').optional(),
})
export type AcceptComplexInvitationsInput = z.infer<typeof acceptComplexInvitationsSchema>
export const revokeComplexUserSchema = z.object({
userId: z.string().min(1, 'El userId no puede estar vacío.'),
})
export type RevokeComplexUserInput = z.infer<typeof revokeComplexUserSchema>
export const selectComplexSchema = z.object({
complexId: z.string().uuid(),
})
@@ -138,3 +191,12 @@ export type CreateComplexPayload = CreateComplexInput
export type CreateComplexResponse = Complex
export type UpdateComplexPayload = UpdateComplexInput
export type UpdateComplexResponse = Complex
export type InviteComplexUserResponse = {
invitation: ComplexInvitation
}
export type AcceptComplexInvitationsResponse = {
acceptedCount: number
}
export type CancelComplexInvitationResponse = {
ok: boolean
}

View File

@@ -27,24 +27,41 @@ export type {
OnboardingVerifyOtpResponse,
} from './onboarding'
export {
acceptComplexInvitationsSchema,
complexInvitationSchema,
complexInvitationStatusSchema,
complexMemberSchema,
complexUsersOverviewSchema,
complexSchema,
complexUserRoleSchema,
complexWithRoleSchema,
createComplexSchema,
inviteComplexUserSchema,
revokeComplexUserSchema,
selectComplexSchema,
updateComplexSchema,
} from './complex'
export type {
AcceptComplexInvitationsInput,
AcceptComplexInvitationsResponse,
CancelComplexInvitationResponse,
Complex,
ComplexInvitation,
ComplexInvitationStatus,
ComplexMember,
ComplexUserRole,
ComplexWithRole,
CreateComplexInput,
CreateComplexPayload,
CreateComplexResponse,
ComplexUsersOverview,
SelectComplexInput,
InviteComplexUserInput,
InviteComplexUserResponse,
UpdateComplexInput,
UpdateComplexPayload,
UpdateComplexResponse,
RevokeComplexUserInput,
} from './complex'
export {
dayOfWeekSchema,