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

@@ -0,0 +1,27 @@
import {
ComplexMembersError,
acceptComplexInvitations,
} from '@/modules/complex/services/complex-members.service';
import type { AppContext } from '@/types/hono';
import type { AcceptComplexInvitationsInput } from '@repo/api-contract';
export async function acceptComplexInvitationsHandler(c: AppContext) {
const user = c.get('user');
const payload = c.req.valid('json' as never) as AcceptComplexInvitationsInput;
try {
const result = await acceptComplexInvitations(user.id, user.email, payload);
return c.json(result);
} catch (error) {
if (error instanceof ComplexMembersError) {
return c.json(
{ message: error.message },
{
status: error.status,
}
);
}
throw error;
}
}

View File

@@ -0,0 +1,27 @@
import {
ComplexMembersError,
cancelComplexInvitation,
} from '@/modules/complex/services/complex-members.service';
import type { AppContext } from '@/types/hono';
import { z } from 'zod';
const cancelInvitationParamsSchema = z.object({
id: z.uuid(),
invitationId: z.uuid(),
});
export async function cancelComplexInvitationHandler(c: AppContext) {
const user = c.get('user');
const params = cancelInvitationParamsSchema.parse(c.req.param());
try {
const result = await cancelComplexInvitation(user.id, params.id, params.invitationId);
return c.json(result);
} catch (error) {
if (error instanceof ComplexMembersError) {
return c.json({ message: error.message }, { status: error.status });
}
throw error;
}
}

View File

@@ -0,0 +1,33 @@
import {
ComplexMembersError,
inviteComplexUser,
} from '@/modules/complex/services/complex-members.service';
import type { AppContext } from '@/types/hono';
import type { InviteComplexUserInput } from '@repo/api-contract';
import { z } from 'zod';
const complexParamsSchema = z.object({
id: z.uuid(),
});
export async function inviteComplexUserHandler(c: AppContext) {
const user = c.get('user');
const params = complexParamsSchema.parse(c.req.param());
const payload = c.req.valid('json' as never) as InviteComplexUserInput;
try {
const result = await inviteComplexUser(user.id, params.id, payload);
return c.json(result, 201);
} catch (error) {
if (error instanceof ComplexMembersError) {
return c.json(
{ message: error.message },
{
status: error.status,
}
);
}
throw error;
}
}

View File

@@ -0,0 +1,14 @@
import { getComplexUsersOverview } from '@/modules/complex/services/complex-members.service';
import type { AppContext } from '@/types/hono';
import { z } from 'zod';
const complexParamsSchema = z.object({
id: z.uuid(),
});
export async function listComplexUsersHandler(c: AppContext) {
const user = c.get('user');
const params = complexParamsSchema.parse(c.req.param());
const result = await getComplexUsersOverview(user.id, params.id);
return c.json(result);
}

View File

@@ -0,0 +1,27 @@
import {
ComplexMembersError,
resendComplexInvitation,
} from '@/modules/complex/services/complex-members.service';
import type { AppContext } from '@/types/hono';
import { z } from 'zod';
const resendInvitationParamsSchema = z.object({
id: z.uuid(),
invitationId: z.uuid(),
});
export async function resendComplexInvitationHandler(c: AppContext) {
const user = c.get('user');
const params = resendInvitationParamsSchema.parse(c.req.param());
try {
const result = await resendComplexInvitation(user.id, params.id, params.invitationId);
return c.json(result);
} catch (error) {
if (error instanceof ComplexMembersError) {
return c.json({ message: error.message }, { status: error.status });
}
throw error;
}
}

View File

@@ -0,0 +1,32 @@
import {
ComplexMembersError,
revokeComplexUser,
} from '@/modules/complex/services/complex-members.service';
import type { AppContext } from '@/types/hono';
import { z } from 'zod';
const revokeParamsSchema = z.object({
id: z.uuid(),
userId: z.string().min(1),
});
export async function revokeComplexUserHandler(c: AppContext) {
const user = c.get('user');
const params = revokeParamsSchema.parse(c.req.param());
try {
const result = await revokeComplexUser(user.id, params.id, { userId: params.userId });
return c.json(result);
} catch (error) {
if (error instanceof ComplexMembersError) {
return c.json(
{ message: error.message },
{
status: error.status,
}
);
}
throw error;
}
}