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

File diff suppressed because one or more lines are too long

View File

@@ -390,6 +390,7 @@ export const ModelName = {
Verification: 'Verification',
Complex: 'Complex',
ComplexUser: 'ComplexUser',
ComplexInvitation: 'ComplexInvitation',
Sport: 'Sport',
Court: 'Court',
CourtAvailability: 'CourtAvailability',
@@ -413,7 +414,7 @@ export type TypeMap<ExtArgs extends runtime.Types.Extensions.InternalArgs = runt
omit: GlobalOmitOptions
}
meta: {
modelProps: "user" | "session" | "account" | "verification" | "complex" | "complexUser" | "sport" | "court" | "courtAvailability" | "courtPriceRule" | "courtBooking" | "onboardingRequest" | "passwordResetRequest" | "plan"
modelProps: "user" | "session" | "account" | "verification" | "complex" | "complexUser" | "complexInvitation" | "sport" | "court" | "courtAvailability" | "courtPriceRule" | "courtBooking" | "onboardingRequest" | "passwordResetRequest" | "plan"
txIsolationLevel: TransactionIsolationLevel
}
model: {
@@ -861,6 +862,80 @@ export type TypeMap<ExtArgs extends runtime.Types.Extensions.InternalArgs = runt
}
}
}
ComplexInvitation: {
payload: Prisma.$ComplexInvitationPayload<ExtArgs>
fields: Prisma.ComplexInvitationFieldRefs
operations: {
findUnique: {
args: Prisma.ComplexInvitationFindUniqueArgs<ExtArgs>
result: runtime.Types.Utils.PayloadToResult<Prisma.$ComplexInvitationPayload> | null
}
findUniqueOrThrow: {
args: Prisma.ComplexInvitationFindUniqueOrThrowArgs<ExtArgs>
result: runtime.Types.Utils.PayloadToResult<Prisma.$ComplexInvitationPayload>
}
findFirst: {
args: Prisma.ComplexInvitationFindFirstArgs<ExtArgs>
result: runtime.Types.Utils.PayloadToResult<Prisma.$ComplexInvitationPayload> | null
}
findFirstOrThrow: {
args: Prisma.ComplexInvitationFindFirstOrThrowArgs<ExtArgs>
result: runtime.Types.Utils.PayloadToResult<Prisma.$ComplexInvitationPayload>
}
findMany: {
args: Prisma.ComplexInvitationFindManyArgs<ExtArgs>
result: runtime.Types.Utils.PayloadToResult<Prisma.$ComplexInvitationPayload>[]
}
create: {
args: Prisma.ComplexInvitationCreateArgs<ExtArgs>
result: runtime.Types.Utils.PayloadToResult<Prisma.$ComplexInvitationPayload>
}
createMany: {
args: Prisma.ComplexInvitationCreateManyArgs<ExtArgs>
result: BatchPayload
}
createManyAndReturn: {
args: Prisma.ComplexInvitationCreateManyAndReturnArgs<ExtArgs>
result: runtime.Types.Utils.PayloadToResult<Prisma.$ComplexInvitationPayload>[]
}
delete: {
args: Prisma.ComplexInvitationDeleteArgs<ExtArgs>
result: runtime.Types.Utils.PayloadToResult<Prisma.$ComplexInvitationPayload>
}
update: {
args: Prisma.ComplexInvitationUpdateArgs<ExtArgs>
result: runtime.Types.Utils.PayloadToResult<Prisma.$ComplexInvitationPayload>
}
deleteMany: {
args: Prisma.ComplexInvitationDeleteManyArgs<ExtArgs>
result: BatchPayload
}
updateMany: {
args: Prisma.ComplexInvitationUpdateManyArgs<ExtArgs>
result: BatchPayload
}
updateManyAndReturn: {
args: Prisma.ComplexInvitationUpdateManyAndReturnArgs<ExtArgs>
result: runtime.Types.Utils.PayloadToResult<Prisma.$ComplexInvitationPayload>[]
}
upsert: {
args: Prisma.ComplexInvitationUpsertArgs<ExtArgs>
result: runtime.Types.Utils.PayloadToResult<Prisma.$ComplexInvitationPayload>
}
aggregate: {
args: Prisma.ComplexInvitationAggregateArgs<ExtArgs>
result: runtime.Types.Utils.Optional<Prisma.AggregateComplexInvitation>
}
groupBy: {
args: Prisma.ComplexInvitationGroupByArgs<ExtArgs>
result: runtime.Types.Utils.Optional<Prisma.ComplexInvitationGroupByOutputType>[]
}
count: {
args: Prisma.ComplexInvitationCountArgs<ExtArgs>
result: runtime.Types.Utils.Optional<Prisma.ComplexInvitationCountAggregateOutputType> | number
}
}
}
Sport: {
payload: Prisma.$SportPayload<ExtArgs>
fields: Prisma.SportFieldRefs
@@ -1578,6 +1653,21 @@ export const ComplexUserScalarFieldEnum = {
export type ComplexUserScalarFieldEnum = (typeof ComplexUserScalarFieldEnum)[keyof typeof ComplexUserScalarFieldEnum]
export const ComplexInvitationScalarFieldEnum = {
id: 'id',
complexId: 'complexId',
email: 'email',
tokenHash: 'tokenHash',
expiresAt: 'expiresAt',
acceptedAt: 'acceptedAt',
revokedAt: 'revokedAt',
createdAt: 'createdAt',
updatedAt: 'updatedAt'
} as const
export type ComplexInvitationScalarFieldEnum = (typeof ComplexInvitationScalarFieldEnum)[keyof typeof ComplexInvitationScalarFieldEnum]
export const SportScalarFieldEnum = {
id: 'id',
name: 'name',
@@ -1972,6 +2062,7 @@ export type GlobalOmitConfig = {
verification?: Prisma.VerificationOmit
complex?: Prisma.ComplexOmit
complexUser?: Prisma.ComplexUserOmit
complexInvitation?: Prisma.ComplexInvitationOmit
sport?: Prisma.SportOmit
court?: Prisma.CourtOmit
courtAvailability?: Prisma.CourtAvailabilityOmit

View File

@@ -57,6 +57,7 @@ export const ModelName = {
Verification: 'Verification',
Complex: 'Complex',
ComplexUser: 'ComplexUser',
ComplexInvitation: 'ComplexInvitation',
Sport: 'Sport',
Court: 'Court',
CourtAvailability: 'CourtAvailability',
@@ -169,6 +170,21 @@ export const ComplexUserScalarFieldEnum = {
export type ComplexUserScalarFieldEnum = (typeof ComplexUserScalarFieldEnum)[keyof typeof ComplexUserScalarFieldEnum]
export const ComplexInvitationScalarFieldEnum = {
id: 'id',
complexId: 'complexId',
email: 'email',
tokenHash: 'tokenHash',
expiresAt: 'expiresAt',
acceptedAt: 'acceptedAt',
revokedAt: 'revokedAt',
createdAt: 'createdAt',
updatedAt: 'updatedAt'
} as const
export type ComplexInvitationScalarFieldEnum = (typeof ComplexInvitationScalarFieldEnum)[keyof typeof ComplexInvitationScalarFieldEnum]
export const SportScalarFieldEnum = {
id: 'id',
name: 'name',