Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
159 changes: 80 additions & 79 deletions apps/app/src/actions/policies/create-new-policy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,91 +7,92 @@ import { authActionClient } from "../safe-action";
import { createPolicySchema } from "../schema";

export const createPolicyAction = authActionClient
.schema(createPolicySchema)
.metadata({
name: "create-policy",
track: {
event: "create-policy",
channel: "server",
},
})
.action(async ({ parsedInput, ctx }) => {
const { title, description, frameworkIds, controlIds } = parsedInput;
const { activeOrganizationId } = ctx.session;
const { user } = ctx;
.schema(createPolicySchema)
.metadata({
name: "create-policy",
track: {
event: "create-policy",
channel: "server",
},
})
.action(async ({ parsedInput, ctx }) => {
const { title, description, frameworkIds, controlIds } = parsedInput;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Verify schema and usage alignment

There appears to be a potential mismatch between the fields being destructured (title, description, frameworkIds, controlIds) and what's defined in the createPolicySchema (which according to the provided snippet only includes name and status).

Please verify that the schema correctly validates all fields being used in this function to prevent potential runtime errors.


🏁 Script executed:

#!/bin/bash
# Check the complete definition of createPolicySchema
echo "Checking createPolicySchema definition:"
cat apps/app/src/lib/schema.ts | grep -A 20 "export const createPolicySchema"

# Look for any field mapping or transformation between schema and action
echo -e "\nChecking for any mapping between schema fields and action parameters:"
grep -r "createPolicySchema" --include="*.ts" --include="*.tsx" apps/app/src/ | grep -v "import"

Length of output: 1169


🏁 Script executed:

#!/bin/bash
# Attempt to output the definition of createPolicySchema from the validations file.
echo "Checking createPolicySchema definition in apps/app/src/lib/validations.ts:"
grep -A 30 "export const createPolicySchema" apps/app/src/lib/validations.ts

Length of output: 498


Action Required: Align Schema Definition with Input Fields

It has been verified that the createPolicySchema in apps/app/src/lib/validations.ts only defines the fields name and status, while the action in apps/app/src/actions/policies/create-new-policy.ts is destructuring title, description, frameworkIds, and controlIds from the parsed input. This discrepancy likely indicates either an omitted transformation step or a misalignment between the expected schema and the actual input used in the action. Please review and update the schema or the input handling accordingly.

  • File: apps/app/src/actions/policies/create-new-policy.ts
    Issue: Destructured fields (title, description, frameworkIds, controlIds) do not match the schema fields (name, status).

const { activeOrganizationId } = ctx.session;
const { user } = ctx;

if (!activeOrganizationId) {
return {
success: false,
error: "Not authorized",
};
}
if (!activeOrganizationId) {
return {
success: false,
error: "Not authorized",
};
}

if (!user) {
return {
success: false,
error: "Not authorized",
};
}
if (!user) {
return {
success: false,
error: "Not authorized",
};
}

try {
// Create the policy
const policy = await db.policy.create({
data: {
name: title,
description,
organizationId: activeOrganizationId,
ownerId: user.id,
department: Departments.none,
frequency: Frequency.monthly,
content: [
{ type: "paragraph", content: [{ type: "text", text: "" }] },
],
},
});
try {
// Create the policy
const policy = await db.policy.create({
data: {
name: title,
description,
organizationId: activeOrganizationId,
ownerId: user.id,
department: Departments.none,
frequency: Frequency.monthly,
content: [
{ type: "paragraph", content: [{ type: "text", text: "" }] },
],
},
});

// Create artifacts for each control
if (controlIds && controlIds.length > 0) {
// Create artifacts that link the policy to controls
await Promise.all(
controlIds.map(async (controlId) => {
// Create the artifact
const artifact = await db.artifact.create({
data: {
type: "policy",
policyId: policy.id,
organizationId: activeOrganizationId,
},
});
// Create artifacts for each control
if (controlIds && controlIds.length > 0) {
// Create artifacts that link the policy to controls
await Promise.all(
controlIds.map(async (controlId) => {
// Create the artifact
const artifact = await db.artifact.create({
data: {
type: "policy",
policyId: policy.id,
organizationId: activeOrganizationId,
},
});

// Connect the artifact to the control
await db.control.update({
where: { id: controlId },
data: {
artifacts: {
connect: { id: artifact.id },
},
},
});
// Connect the artifact to the control
await db.control.update({
where: { id: controlId },
data: {
artifacts: {
connect: { id: artifact.id },
},
},
});

return artifact;
})
);
}
return artifact;
}),
);
}

revalidatePath(`/${activeOrganizationId}/policies`);
revalidateTag("policies");
revalidatePath(`/${activeOrganizationId}/policies/all`);
revalidatePath(`/${activeOrganizationId}/policies`);
revalidateTag("policies");

return {
success: true,
policyId: policy.id,
};
} catch (error) {
console.error(error);
return {
success: true,
policyId: policy.id,
};
} catch (error) {
console.error(error);

return {
success: false,
error: "Failed to create policy",
};
}
});
return {
success: false,
error: "Failed to create policy",
};
}
});
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,11 @@ export default async function Layout({
children: React.ReactNode;
}) {
const t = await getI18n();

const session = await auth.api.getSession({
headers: await headers(),
});
const user = session?.user;

const orgId = session?.session.activeOrganizationId;

return (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,16 @@
import { ColumnDef } from "@tanstack/react-table";
import { Policy } from "@bubba/db/types";
import { DataTableColumnHeader } from "@/components/data-table/data-table-column-header";
import { StatusPolicies } from "@/components/status-policies";
import { formatDate } from "@/lib/format";

export function getPolicyColumns(): ColumnDef<Policy>[] {
return [
{
id: "name",
accessorKey: "name",
header: ({ column }) => (
<DataTableColumnHeader column={column} title="Name" />
<DataTableColumnHeader column={column} title="Policy Name" />
),
cell: ({ row }) => {
return (
Expand All @@ -22,8 +24,8 @@ export function getPolicyColumns(): ColumnDef<Policy>[] {
);
},
meta: {
label: "Name",
placeholder: "Search names...",
label: "Policy Name",
placeholder: "Search for a policy...",
variant: "text",
},
enableColumnFilter: true,
Expand All @@ -35,17 +37,32 @@ export function getPolicyColumns(): ColumnDef<Policy>[] {
<DataTableColumnHeader column={column} title="Status" />
),
cell: ({ row }) => {
return (
<p className="text-sm font-medium">
{row.getValue("status")}
</p>
)
return <StatusPolicies status={row.getValue("status")} />;
},
meta: {
label: "Status",
placeholder: "Search status...",
variant: "select",
},
},
{
id: "updatedAt",
accessorKey: "updatedAt",
header: ({ column }) => (
<DataTableColumnHeader column={column} title="Last Updated" />
),
cell: ({ row }) => {
return (
<div className="text-muted-foreground">
{formatDate(row.getValue("updatedAt"))}
</div>
);
},
meta: {
label: "Last Updated",
placeholder: "Search last updated...",
variant: "date",
},
},
];
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { getPolicies } from "../data/queries";
import { DataTableSortList } from "@/components/data-table/data-table-sort-list";
import { getPolicyColumns } from "./policies-table-columns";
import { DataTableToolbar } from "@/components/data-table/data-table-toolbar";
import { CreatePolicySheet } from "@/components/sheets/create-policy-sheet";

interface PoliciesTableProps {
promises: Promise<[
Expand Down Expand Up @@ -37,11 +38,13 @@ export function PoliciesTable({ promises }: PoliciesTableProps) {
<>
<DataTable
table={table}
getRowId={(row) => row.id}
>
<DataTableToolbar table={table}>
<DataTableToolbar table={table} sheet="create-policy-sheet" action="Create Policy">
<DataTableSortList table={table} align="end" />
</DataTableToolbar>
</DataTable>
<CreatePolicySheet />
</>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,9 @@ import "server-only";

import { db } from "@bubba/db";
import { auth } from "@bubba/auth";
import type { GetPolicySchema } from "@/lib/validations";
import type { GetPolicySchema } from "./validations";
import { cache } from "react";
import { headers } from "next/headers";
import { getValidFilters } from "@/lib/data-table";
import { Prisma } from "@prisma/client";

export async function getPolicies(input: GetPolicySchema) {
Expand All @@ -30,6 +29,11 @@ export async function getPolicies(input: GetPolicySchema) {
mode: Prisma.QueryMode.insensitive,
},
}),
...(input.status.length > 0 && {
status: {
in: input.status,
},
}),
};

const policies = await db.policy.findMany({
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import {
createSearchParamsCache,
parseAsArrayOf,
parseAsInteger,
parseAsString,
parseAsStringEnum,
} from "nuqs/server";
import * as z from "zod";
import { getFiltersStateParser, getSortingStateParser } from "@/lib/parsers";
import { Policy, PolicyStatus } from "@bubba/db/types";

export const searchParamsCache = createSearchParamsCache({
page: parseAsInteger.withDefault(1),
perPage: parseAsInteger.withDefault(10),
sort: getSortingStateParser<Policy>().withDefault([
{ id: "createdAt", desc: true },
]),
name: parseAsString.withDefault(""),
status: parseAsArrayOf(z.nativeEnum(PolicyStatus)).withDefault([]),
createdAt: parseAsArrayOf(z.coerce.date()).withDefault([]),
updatedAt: parseAsArrayOf(z.coerce.date()).withDefault([]),
// advanced filter
filters: getFiltersStateParser().withDefault([]),
joinOperator: parseAsStringEnum(["and", "or"]).withDefault("and"),
});

export type GetPolicySchema = Awaited<
ReturnType<typeof searchParamsCache.parse>
>;
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { getI18n } from "@/locales/server";
import type { Metadata } from "next";
import { setStaticParamsLocale } from "next-international/server";
import { DataTableSkeleton } from "@/components/data-table/data-table-skeleton";
import React from "react";
import { Suspense } from "react";
import type { SearchParams } from "@/types";
import { searchParamsCache } from "@/lib/validations";
import { getValidFilters } from "@/lib/data-table";
Expand Down Expand Up @@ -33,7 +33,7 @@ export default async function PoliciesPage({
]);

return (
<React.Suspense
<Suspense
fallback={
<DataTableSkeleton
columnCount={7}
Expand All @@ -52,7 +52,7 @@ export default async function PoliciesPage({
}
>
<PoliciesTable promises={promises} />
</React.Suspense>
</Suspense>
);
}

Expand Down
Loading