feat(groups): implement group creation functionality and refactor related components
- Removed organization dependency from group creation logic. - Introduced new route and use-case for creating groups. - Refactored form handling for group creation into a reusable GroupForm component. - Updated API calls to support new group creation endpoint. - Adjusted tests to reflect changes in group creation logic and validation. - Removed obsolete organizations route and related components. - Updated breadcrumb navigation and routing for group management.
This commit is contained in:
@@ -4,14 +4,13 @@ import { Hono } from 'hono';
|
||||
const db = {
|
||||
group: {
|
||||
findMany: mock(),
|
||||
findFirst: mock(),
|
||||
count: mock(),
|
||||
create: mock(),
|
||||
},
|
||||
groupMember: {
|
||||
create: mock(),
|
||||
},
|
||||
organization: {
|
||||
merchantAccount: {
|
||||
findUnique: mock(),
|
||||
},
|
||||
};
|
||||
@@ -102,19 +101,29 @@ describe('groups routes', () => {
|
||||
expect(res.status).toBe(400);
|
||||
});
|
||||
|
||||
it('creates a group from an owned organization', async () => {
|
||||
const organization = {
|
||||
id: 'org-1',
|
||||
name: 'Escuela Alfa',
|
||||
members: [{ userId, role: 'owner' }],
|
||||
};
|
||||
prisma.organization.findUnique.mockResolvedValue(organization as never);
|
||||
prisma.group.findFirst.mockResolvedValue(null);
|
||||
prisma.group.create.mockResolvedValue(group);
|
||||
it('creates a group with billing configuration', async () => {
|
||||
prisma.merchantAccount.findUnique.mockResolvedValue({ id: 'merchant-1', userId });
|
||||
prisma.group.create.mockResolvedValue({
|
||||
...group,
|
||||
days: ['MONDAY'],
|
||||
time: '09:00',
|
||||
capacity: 20,
|
||||
price: 500,
|
||||
billingType: 'MONTHLY',
|
||||
dueDay: 5,
|
||||
});
|
||||
|
||||
const res = await makeApp({ id: userId }).request('/groups/from-organization', {
|
||||
const res = await makeApp({ id: userId }).request('/groups', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ organizationId: 'org-1' }),
|
||||
body: JSON.stringify({
|
||||
name: 'Cuadrilla Alfa',
|
||||
days: ['MONDAY'],
|
||||
time: '09:00',
|
||||
capacity: 20,
|
||||
price: 500,
|
||||
billingType: 'MONTHLY',
|
||||
dueDay: 5,
|
||||
}),
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
});
|
||||
|
||||
@@ -122,77 +131,63 @@ describe('groups routes', () => {
|
||||
expect(await res.json()).toEqual({
|
||||
group: {
|
||||
...group,
|
||||
days: ['MONDAY'],
|
||||
time: '09:00',
|
||||
capacity: 20,
|
||||
price: 500,
|
||||
billingType: 'MONTHLY',
|
||||
dueDay: 5,
|
||||
createdAt: group.createdAt.toISOString(),
|
||||
updatedAt: group.updatedAt.toISOString(),
|
||||
},
|
||||
alreadyExists: false,
|
||||
});
|
||||
expect(prisma.group.create).toHaveBeenCalledWith({
|
||||
data: { name: 'Escuela Alfa', createdById: userId },
|
||||
data: {
|
||||
name: 'Cuadrilla Alfa',
|
||||
createdById: userId,
|
||||
days: ['MONDAY'],
|
||||
time: '09:00',
|
||||
capacity: 20,
|
||||
price: 500,
|
||||
billingType: 'MONTHLY',
|
||||
dueDay: 5,
|
||||
},
|
||||
});
|
||||
expect(prisma.groupMember.create).toHaveBeenCalledWith({
|
||||
data: { groupId: 'group-1', userId, role: 'OWNER' },
|
||||
});
|
||||
});
|
||||
|
||||
it('reuses an existing group with the same name', async () => {
|
||||
const organization = {
|
||||
id: 'org-1',
|
||||
name: 'Escuela Alfa',
|
||||
members: [{ userId, role: 'owner' }],
|
||||
};
|
||||
prisma.organization.findUnique.mockResolvedValue(organization as never);
|
||||
prisma.group.findFirst.mockResolvedValue(group);
|
||||
it('rejects creating a group without a linked merchant account', async () => {
|
||||
prisma.merchantAccount.findUnique.mockResolvedValue(null);
|
||||
|
||||
const res = await makeApp({ id: userId }).request('/groups/from-organization', {
|
||||
const res = await makeApp({ id: userId }).request('/groups', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ organizationId: 'org-1' }),
|
||||
body: JSON.stringify({
|
||||
name: 'Cuadrilla Alfa',
|
||||
days: ['MONDAY'],
|
||||
time: '09:00',
|
||||
capacity: 20,
|
||||
price: 500,
|
||||
billingType: 'MONTHLY',
|
||||
dueDay: 5,
|
||||
}),
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
});
|
||||
|
||||
expect(res.status).toBe(200);
|
||||
const body = await res.json();
|
||||
expect(body.alreadyExists).toBe(true);
|
||||
expect(res.status).toBe(409);
|
||||
expect(await res.json()).toMatchObject({ code: 'payment_not_setup' });
|
||||
expect(prisma.group.create).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('rejects creating a group for an unknown organization', async () => {
|
||||
prisma.organization.findUnique.mockResolvedValue(null);
|
||||
|
||||
const res = await makeApp({ id: userId }).request('/groups/from-organization', {
|
||||
it('rejects creating a group with an invalid payload', async () => {
|
||||
const res = await makeApp({ id: userId }).request('/groups', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ organizationId: 'missing' }),
|
||||
body: JSON.stringify({ name: 'x' }),
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
});
|
||||
|
||||
expect(res.status).toBe(404);
|
||||
expect(await res.json()).toMatchObject({ code: 'organization_not_found' });
|
||||
expect(res.status).toBe(400);
|
||||
expect(prisma.group.create).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('rejects creating a group when the user is not the owner', async () => {
|
||||
const organization = {
|
||||
id: 'org-1',
|
||||
name: 'Escuela Alfa',
|
||||
members: [{ userId: 'other-user', role: 'owner' }],
|
||||
};
|
||||
prisma.organization.findUnique.mockResolvedValue(organization as never);
|
||||
|
||||
const res = await makeApp({ id: userId }).request('/groups/from-organization', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ organizationId: 'org-1' }),
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
});
|
||||
|
||||
expect(res.status).toBe(403);
|
||||
expect(await res.json()).toMatchObject({ code: 'group_owner_required' });
|
||||
expect(prisma.group.create).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('rejects creating a group without a session', async () => {
|
||||
const res = await makeApp(null).request('/groups/from-organization', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ organizationId: 'org-1' }),
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
});
|
||||
|
||||
expect(res.status).toBe(401);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user