Skip to content

Commit 943a7d3

Browse files
feat: implement control creation functionality with UI integration (#1497)
- Added a new action for creating controls with input validation using Zod. - Introduced CreateControlSheet component for the control creation UI, integrating form handling and submission. - Enhanced ControlsPage to fetch policies, tasks, and requirements for control assignment. - Updated ControlsTable to include the new CreateControlSheet for better user experience. These changes improve control management capabilities within the application. Co-authored-by: Mariano Fuentes <[email protected]>
1 parent 2f203be commit 943a7d3

File tree

5 files changed

+692
-12
lines changed

5 files changed

+692
-12
lines changed
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
'use server';
2+
3+
import { authActionClient } from '@/actions/safe-action';
4+
import { db } from '@db';
5+
import { revalidatePath } from 'next/cache';
6+
import { headers } from 'next/headers';
7+
import { z } from 'zod';
8+
9+
const createControlSchema = z.object({
10+
name: z.string().min(1, {
11+
message: 'Name is required',
12+
}),
13+
description: z.string().min(1, {
14+
message: 'Description is required',
15+
}),
16+
policyIds: z.array(z.string()).optional(),
17+
taskIds: z.array(z.string()).optional(),
18+
requirementMappings: z
19+
.array(
20+
z.object({
21+
requirementId: z.string(),
22+
frameworkInstanceId: z.string(),
23+
}),
24+
)
25+
.optional(),
26+
});
27+
28+
export const createControlAction = authActionClient
29+
.inputSchema(createControlSchema)
30+
.metadata({
31+
name: 'create-control',
32+
track: {
33+
event: 'create-control',
34+
channel: 'server',
35+
},
36+
})
37+
.action(async ({ parsedInput, ctx }) => {
38+
const { name, description, policyIds, taskIds, requirementMappings } = parsedInput;
39+
const {
40+
session: { activeOrganizationId },
41+
user,
42+
} = ctx;
43+
44+
if (!user.id || !activeOrganizationId) {
45+
throw new Error('Invalid user input');
46+
}
47+
48+
try {
49+
const control = await db.control.create({
50+
data: {
51+
name,
52+
description,
53+
organizationId: activeOrganizationId,
54+
...(policyIds &&
55+
policyIds.length > 0 && {
56+
policies: {
57+
connect: policyIds.map((id) => ({ id })),
58+
},
59+
}),
60+
...(taskIds &&
61+
taskIds.length > 0 && {
62+
tasks: {
63+
connect: taskIds.map((id) => ({ id })),
64+
},
65+
}),
66+
// Note: Requirements mapping is handled through RequirementMap table
67+
},
68+
});
69+
70+
// Handle requirement mappings separately if provided
71+
if (requirementMappings && requirementMappings.length > 0) {
72+
await Promise.all(
73+
requirementMappings.map((mapping) =>
74+
db.requirementMap.create({
75+
data: {
76+
controlId: control.id,
77+
requirementId: mapping.requirementId,
78+
frameworkInstanceId: mapping.frameworkInstanceId,
79+
},
80+
}),
81+
),
82+
);
83+
}
84+
85+
// Revalidate the path based on the header
86+
const headersList = await headers();
87+
let path = headersList.get('x-pathname') || headersList.get('referer') || '';
88+
path = path.replace(/\/[a-z]{2}\//, '/');
89+
revalidatePath(path);
90+
91+
return {
92+
success: true,
93+
control,
94+
};
95+
} catch (error) {
96+
console.error('Failed to create control:', error);
97+
return {
98+
success: false,
99+
error: 'Failed to create control',
100+
};
101+
}
102+
});

0 commit comments

Comments
 (0)