diff --git a/apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/(home)/components/FrameworkProgress.tsx b/apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/(home)/components/FrameworkProgress.tsx index 87cf230595..c9c3b3cee6 100644 --- a/apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/(home)/components/FrameworkProgress.tsx +++ b/apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/(home)/components/FrameworkProgress.tsx @@ -106,7 +106,7 @@ export function FrameworkProgress({ frameworks }: Props) { {t("frameworks.overview.progress.empty.description")}

diff --git a/apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/(home)/components/RequirementStatusChart.tsx b/apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/(home)/components/RequirementStatusChart.tsx index c875b7a065..13345995f1 100644 --- a/apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/(home)/components/RequirementStatusChart.tsx +++ b/apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/(home)/components/RequirementStatusChart.tsx @@ -1,178 +1,177 @@ "use client"; import { useI18n } from "@/locales/client"; -import type { - Framework, - OrganizationFramework, -} from "@bubba/db/types"; +import type { Framework, OrganizationFramework } from "@bubba/db/types"; import { Card, CardContent, CardHeader, CardTitle } from "@bubba/ui/card"; import { Progress } from "@bubba/ui/progress"; import Link from "next/link"; import { useMemo } from "react"; import type { OrganizationControlType } from "../overview/frameworks/[frameworkId]/components/table/FrameworkControlsTableColumns"; import { useOrganizationCategories } from "../overview/frameworks/[frameworkId]/hooks/useOrganizationCategories"; +import { useParams } from "next/navigation"; interface Props { - frameworks: (OrganizationFramework & { - framework: Framework; - })[]; + frameworks: (OrganizationFramework & { + framework: Framework; + })[]; } // Helper function to check if a control is compliant based on its requirements const isControlCompliant = (control: OrganizationControlType) => { - // First, check if the control has the direct status of "compliant" - if (control.status === "compliant") { - return true; - } - - // Then check the requirements if they exist - const requirements = control.requirements; - - if (!requirements || requirements.length === 0) { - return false; - } - - const totalRequirements = requirements.length; - const completedRequirements = requirements.filter((req) => { - let isCompleted = false; - - switch (req.type) { - case "policy": - isCompleted = req.organizationPolicy?.status === "published"; - break; - case "file": - isCompleted = !!req.fileUrl; - break; - case "evidence": - isCompleted = req.organizationEvidence?.published === true; - break; - default: - isCompleted = req.published || false; - } - - return isCompleted; - }).length; - - return completedRequirements === totalRequirements; + // First, check if the control has the direct status of "compliant" + if (control.status === "compliant") { + return true; + } + + // Then check the requirements if they exist + const requirements = control.requirements; + + if (!requirements || requirements.length === 0) { + return false; + } + + const totalRequirements = requirements.length; + const completedRequirements = requirements.filter((req) => { + let isCompleted = false; + + switch (req.type) { + case "policy": + isCompleted = req.organizationPolicy?.status === "published"; + break; + case "file": + isCompleted = !!req.fileUrl; + break; + case "evidence": + isCompleted = req.organizationEvidence?.published === true; + break; + default: + isCompleted = req.published || false; + } + + return isCompleted; + }).length; + + return completedRequirements === totalRequirements; }; // Individual FrameworkCard component function FrameworkCard({ - framework, + framework, }: { framework: OrganizationFramework & { framework: Framework } }) { - const { data: organizationCategories, isLoading } = useOrganizationCategories( - framework.framework.id, - ); - - // Transform the organizationCategories into controls - const controls = useMemo(() => { - if (!organizationCategories) return []; - - return organizationCategories.flatMap((category) => - category.organizationControl.map((control) => ({ - code: control.control.code, - description: control.control.description, - name: control.control.name, - status: control.status, - id: control.id, - frameworkId: framework.framework.id, - category: category.name, - requirements: control.OrganizationControlRequirement, - })), - ); - }, [ - organizationCategories, - framework.framework.id, - ]) as OrganizationControlType[]; - - // Calculate framework compliance based on controls - const compliance = useMemo(() => { - if (isLoading || controls.length === 0) return 0; - - const totalControls = controls.length; - const compliantControls = controls.filter(isControlCompliant).length; - - return totalControls > 0 - ? Math.round((compliantControls / totalControls) * 100) - : 0; - }, [controls, isLoading]); - - if (isLoading) { - return ( -
-
-
-
-
-
-
- ); - } - - return ( - -
-
- {framework.framework.name.substring(0, 2).toUpperCase()} -
-
-
-
-

{framework.framework.name}

- - {compliance}% Compliant - -
- -
- - ); + const { data: organizationCategories, isLoading } = useOrganizationCategories( + framework.framework.id, + ); + const { orgId } = useParams<{ orgId: string }>(); + + // Transform the organizationCategories into controls + const controls = useMemo(() => { + if (!organizationCategories) return []; + + return organizationCategories.flatMap((category) => + category.organizationControl.map((control) => ({ + code: control.control.code, + description: control.control.description, + name: control.control.name, + status: control.status, + id: control.id, + frameworkId: framework.framework.id, + category: category.name, + requirements: control.OrganizationControlRequirement, + })), + ); + }, [ + organizationCategories, + framework.framework.id, + ]) as OrganizationControlType[]; + + // Calculate framework compliance based on controls + const compliance = useMemo(() => { + if (isLoading || controls.length === 0) return 0; + + const totalControls = controls.length; + const compliantControls = controls.filter(isControlCompliant).length; + + return totalControls > 0 + ? Math.round((compliantControls / totalControls) * 100) + : 0; + }, [controls, isLoading]); + + if (isLoading) { + return ( +
+
+
+
+
+
+
+ ); + } + + return ( + +
+
+ {framework.framework.name.substring(0, 2).toUpperCase()} +
+
+
+
+

{framework.framework.name}

+ + {compliance}% Compliant + +
+ +
+ + ); } // Main component export function RequirementStatus({ frameworks }: Props) { - const t = useI18n(); - const isLoading = !frameworks; - - return ( - - - {t("frameworks.title")} - - - {isLoading ? ( -
-
-

- Loading compliance data... -

-
- ) : frameworks.length === 0 ? ( -
-

- {t("frameworks.overview.empty.description")} -

-
- ) : ( -
- {/* Framework List */} -
- {frameworks.map((framework) => ( - - ))} -
-
- )} - - - ); + const t = useI18n(); + const isLoading = !frameworks; + + return ( + + + {t("frameworks.title")} + + + {isLoading ? ( +
+
+

+ Loading compliance data... +

+
+ ) : frameworks.length === 0 ? ( +
+

+ {t("frameworks.overview.empty.description")} +

+
+ ) : ( +
+ {/* Framework List */} +
+ {frameworks.map((framework) => ( + + ))} +
+
+ )} + + + ); } diff --git a/apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/(home)/overview/frameworks/[frameworkId]/components/table/FrameworkControlsTableColumns.tsx b/apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/(home)/overview/frameworks/[frameworkId]/components/table/FrameworkControlsTableColumns.tsx index 03a34c3d78..3407b7eba8 100644 --- a/apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/(home)/overview/frameworks/[frameworkId]/components/table/FrameworkControlsTableColumns.tsx +++ b/apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/(home)/overview/frameworks/[frameworkId]/components/table/FrameworkControlsTableColumns.tsx @@ -17,7 +17,7 @@ import { TooltipProvider, TooltipTrigger, } from "@bubba/ui/tooltip"; - +import { useParams } from "next/navigation"; export type OrganizationControlType = { code: string; description: string | null; @@ -62,6 +62,7 @@ function getControlStatus( export function FrameworkControlsTableColumns(): ColumnDef[] { const t = useI18n(); + const { orgId } = useParams<{ orgId: string }>(); return [ { @@ -72,7 +73,7 @@ export function FrameworkControlsTableColumns(): ColumnDef {row.original.name} diff --git a/apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/risk/[riskId]/comments/page.tsx b/apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/risk/[riskId]/comments/page.tsx index b1470f9224..27a5060628 100644 --- a/apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/risk/[riskId]/comments/page.tsx +++ b/apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/risk/[riskId]/comments/page.tsx @@ -8,79 +8,79 @@ import { unstable_cache } from "next/cache"; import { redirect } from "next/navigation"; interface PageProps { - params: Promise<{ riskId: string }>; + params: Promise<{ riskId: string }>; } export default async function RiskPage({ params }: PageProps) { - const session = await auth(); - const { riskId } = await params; + const session = await auth(); + const { riskId } = await params; - if (!session) { - redirect("/auth"); - } + if (!session) { + redirect("/auth"); + } - if (!session.user.organizationId || !riskId) { - redirect("/"); - } + if (!session.user.organizationId || !riskId) { + redirect("/"); + } - const risk = await getRisk(riskId, session.user.organizationId); + const risk = await getRisk(riskId, session.user.organizationId); - if (!risk) { - redirect("/risk"); - } + if (!risk) { + redirect(`/${session.user.organizationId}/risk`); + } - const users = await getUsers(session.user.organizationId); + const users = await getUsers(session.user.organizationId); - return ( -
- -
- ); + return ( +
+ +
+ ); } const getRisk = unstable_cache( - async (riskId: string, organizationId: string) => { - const risk = await db.risk.findUnique({ - where: { - id: riskId, - organizationId: organizationId, - }, - include: { - owner: true, - comments: { - orderBy: { - createdAt: "desc", - }, - }, - }, - }); + async (riskId: string, organizationId: string) => { + const risk = await db.risk.findUnique({ + where: { + id: riskId, + organizationId: organizationId, + }, + include: { + owner: true, + comments: { + orderBy: { + createdAt: "desc", + }, + }, + }, + }); - return risk; - }, - ["risk-cache"], + return risk; + }, + ["risk-cache"], ); const getUsers = unstable_cache( - async (organizationId: string) => { - const users = await db.user.findMany({ - where: { organizationId: organizationId }, - }); + async (organizationId: string) => { + const users = await db.user.findMany({ + where: { organizationId: organizationId }, + }); - return users; - }, - ["users-cache"], + return users; + }, + ["users-cache"], ); export async function generateMetadata({ - params, + params, }: { - params: Promise<{ locale: string }>; + params: Promise<{ locale: string }>; }): Promise { - const { locale } = await params; - setStaticParamsLocale(locale); - const t = await getI18n(); + const { locale } = await params; + setStaticParamsLocale(locale); + const t = await getI18n(); - return { - title: t("sub_pages.risk.risk_comments"), - }; + return { + title: t("sub_pages.risk.risk_comments"), + }; } diff --git a/apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/risk/[riskId]/layout.tsx b/apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/risk/[riskId]/layout.tsx index bf9900e16a..f85b4ce8cc 100644 --- a/apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/risk/[riskId]/layout.tsx +++ b/apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/risk/[riskId]/layout.tsx @@ -23,7 +23,7 @@ export default async function Layout({ children, params }: LayoutProps) { const risk = await getRisk(riskId.riskId, session.user.organizationId); if (!risk) { - redirect("/risk"); + redirect(`/${session.user.organizationId}/risk`); } const orgId = session.user.organizationId; diff --git a/apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/risk/[riskId]/page.tsx b/apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/risk/[riskId]/page.tsx index 763bc54db4..8c558ec1ad 100644 --- a/apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/risk/[riskId]/page.tsx +++ b/apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/risk/[riskId]/page.tsx @@ -6,8 +6,8 @@ import { RiskOverview } from "@/components/risks/risk-overview"; import type { RiskTaskType } from "@/components/tables/risk-tasks/columns"; import { DataTable } from "@/components/tables/risk-tasks/data-table"; import { - NoResults, - NoTasks, + NoResults, + NoTasks, } from "@/components/tables/risk-tasks/empty-states"; import { FilterToolbar } from "@/components/tables/risk-tasks/filter-toolbar"; import { getServerColumnHeaders } from "@/components/tables/risk-tasks/server-columns"; @@ -21,231 +21,231 @@ import { unstable_cache } from "next/cache"; import { redirect } from "next/navigation"; interface PageProps { - searchParams: Promise<{ - search?: string; - status?: string; - sort?: string; - page?: string; - per_page?: string; - }>; - params: Promise<{ riskId: string; locale: string }>; + searchParams: Promise<{ + search?: string; + status?: string; + sort?: string; + page?: string; + per_page?: string; + }>; + params: Promise<{ riskId: string; locale: string }>; } export default async function RiskPage({ searchParams, params }: PageProps) { - const session = await auth(); - const t = await getI18n(); - - const { - search, - status, - sort, - page = "1", - per_page = "5", - } = await searchParams; - - const { riskId } = await params; - const columnHeaders = await getServerColumnHeaders(); - const [column, order] = sort?.split(":") ?? []; - const hasFilters = !!(search || status); - const { tasks: loadedTasks, total } = await getTasks({ - riskId, - search, - status: status as RiskTaskStatus, - column, - order, - page: Number.parseInt(page), - per_page: Number.parseInt(per_page), - }); - - if (!session) { - redirect("/auth"); - } - - if (!session.user.organizationId || !riskId) { - redirect("/"); - } - - const risk = await getRisk(riskId, session.user.organizationId); - - if (!risk) { - redirect("/risk"); - } - - const users = await getUsers(session.user.organizationId); - - return ( -
- - - - - -
- {t("risk.tasks.title")} -
-
-
- -
- - {loadedTasks.length > 0 ? ( - - ) : hasFilters ? ( - - ) : ( - <> - - - - )} -
-
-
- -
- - -
-
- ); + const session = await auth(); + const t = await getI18n(); + + const { + search, + status, + sort, + page = "1", + per_page = "5", + } = await searchParams; + + const { riskId } = await params; + const columnHeaders = await getServerColumnHeaders(); + const [column, order] = sort?.split(":") ?? []; + const hasFilters = !!(search || status); + const { tasks: loadedTasks, total } = await getTasks({ + riskId, + search, + status: status as RiskTaskStatus, + column, + order, + page: Number.parseInt(page), + per_page: Number.parseInt(per_page), + }); + + if (!session) { + redirect("/auth"); + } + + if (!session.user.organizationId || !riskId) { + redirect("/"); + } + + const risk = await getRisk(riskId, session.user.organizationId); + + if (!risk) { + redirect(`/${session.user.organizationId}/risk`); + } + + const users = await getUsers(session.user.organizationId); + + return ( +
+ + + + + +
+ {t("risk.tasks.title")} +
+
+
+ +
+ + {loadedTasks.length > 0 ? ( + + ) : hasFilters ? ( + + ) : ( + <> + + + + )} +
+
+
+ +
+ + +
+
+ ); } const getRisk = unstable_cache( - async (riskId: string, organizationId: string) => { - const risk = await db.risk.findUnique({ - where: { - id: riskId, - organizationId: organizationId, - }, - include: { - owner: true, - }, - }); - - return risk; - }, - ["risk-cache"], + async (riskId: string, organizationId: string) => { + const risk = await db.risk.findUnique({ + where: { + id: riskId, + organizationId: organizationId, + }, + include: { + owner: true, + }, + }); + + return risk; + }, + ["risk-cache"], ); const getTasks = unstable_cache( - async function tasks({ - riskId, - search, - status, - column, - order, - page = 1, - per_page = 10, - }: { - riskId: string; - search?: string; - status?: RiskTaskStatus; - column?: string; - order?: string; - page?: number; - per_page?: number; - }) { - const skip = (page - 1) * per_page; - - const [tasks, total] = await Promise.all([ - db.riskMitigationTask - .findMany({ - where: { - riskId, - AND: [ - search - ? { - OR: [ - { title: { contains: search, mode: "insensitive" } }, - { - description: { contains: search, mode: "insensitive" }, - }, - ], - } - : {}, - status ? { status } : {}, - ], - }, - orderBy: column - ? { - [column]: order === "asc" ? "asc" : "desc", - } - : { - createdAt: "desc", - }, - skip, - take: per_page, - include: { - owner: { - select: { - name: true, - image: true, - }, - }, - }, - }) - .then((tasks) => - tasks.map( - (task) => - ({ - ...task, - dueDate: task.dueDate?.toISOString() ?? "", - owner: { - name: task.owner?.name ?? "", - image: task.owner?.image ?? "", - }, - }) as RiskTaskType, - ), - ), - db.riskMitigationTask.count({ - where: { - riskId, - AND: [ - search - ? { - OR: [ - { title: { contains: search, mode: "insensitive" } }, - { description: { contains: search, mode: "insensitive" } }, - ], - } - : {}, - status ? { status } : {}, - ], - }, - }), - ]); - - return { tasks, total }; - }, - ["tasks-cache"], + async function tasks({ + riskId, + search, + status, + column, + order, + page = 1, + per_page = 10, + }: { + riskId: string; + search?: string; + status?: RiskTaskStatus; + column?: string; + order?: string; + page?: number; + per_page?: number; + }) { + const skip = (page - 1) * per_page; + + const [tasks, total] = await Promise.all([ + db.riskMitigationTask + .findMany({ + where: { + riskId, + AND: [ + search + ? { + OR: [ + { title: { contains: search, mode: "insensitive" } }, + { + description: { contains: search, mode: "insensitive" }, + }, + ], + } + : {}, + status ? { status } : {}, + ], + }, + orderBy: column + ? { + [column]: order === "asc" ? "asc" : "desc", + } + : { + createdAt: "desc", + }, + skip, + take: per_page, + include: { + owner: { + select: { + name: true, + image: true, + }, + }, + }, + }) + .then((tasks) => + tasks.map( + (task) => + ({ + ...task, + dueDate: task.dueDate?.toISOString() ?? "", + owner: { + name: task.owner?.name ?? "", + image: task.owner?.image ?? "", + }, + }) as RiskTaskType, + ), + ), + db.riskMitigationTask.count({ + where: { + riskId, + AND: [ + search + ? { + OR: [ + { title: { contains: search, mode: "insensitive" } }, + { description: { contains: search, mode: "insensitive" } }, + ], + } + : {}, + status ? { status } : {}, + ], + }, + }), + ]); + + return { tasks, total }; + }, + ["tasks-cache"], ); const getUsers = unstable_cache( - async (organizationId: string) => { - const users = await db.user.findMany({ - where: { organizationId: organizationId }, - }); - - return users; - }, - ["users-cache"], + async (organizationId: string) => { + const users = await db.user.findMany({ + where: { organizationId: organizationId }, + }); + + return users; + }, + ["users-cache"], ); export async function generateMetadata({ - params, + params, }: { - params: Promise<{ locale: string }>; + params: Promise<{ locale: string }>; }): Promise { - const { locale } = await params; - setStaticParamsLocale(locale); - const t = await getI18n(); + const { locale } = await params; + setStaticParamsLocale(locale); + const t = await getI18n(); - return { - title: t("sub_pages.risk.risk_overview"), - }; + return { + title: t("sub_pages.risk.risk_overview"), + }; } diff --git a/apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/risk/[riskId]/tasks/[taskId]/page.tsx b/apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/risk/[riskId]/tasks/[taskId]/page.tsx index 532a3335a7..fca0fbf411 100644 --- a/apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/risk/[riskId]/tasks/[taskId]/page.tsx +++ b/apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/risk/[riskId]/tasks/[taskId]/page.tsx @@ -10,78 +10,78 @@ import { unstable_cache } from "next/cache"; import { redirect } from "next/navigation"; interface PageProps { - params: Promise<{ riskId: string; taskId: string }>; + params: Promise<{ riskId: string; taskId: string }>; } export default async function RiskPage({ params }: PageProps) { - const session = await auth(); - const { riskId, taskId } = await params; + const session = await auth(); + const { riskId, taskId } = await params; - if (!session) { - redirect("/auth"); - } + if (!session) { + redirect("/auth"); + } - if (!session.user.organizationId || !riskId) { - redirect("/"); - } + if (!session.user.organizationId || !riskId) { + redirect("/"); + } - const task = await getTask(riskId, taskId); + const task = await getTask(riskId, taskId); - if (!task) { - redirect("/risk"); - } + if (!task) { + redirect(`/${session.user.organizationId}/risk`); + } - const users = await getUsers(session.user.organizationId); + const users = await getUsers(session.user.organizationId); - return ( -
- - - -
- ); + return ( +
+ + + +
+ ); } const getTask = unstable_cache( - async (riskId: string, taskId: string) => { - const task = await db.riskMitigationTask.findUnique({ - where: { - riskId: riskId, - id: taskId, - }, - include: { - owner: true, - TaskAttachment: true, - TaskComments: true, - }, - }); + async (riskId: string, taskId: string) => { + const task = await db.riskMitigationTask.findUnique({ + where: { + riskId: riskId, + id: taskId, + }, + include: { + owner: true, + TaskAttachment: true, + TaskComments: true, + }, + }); - return task; - }, - ["risk-cache"], + return task; + }, + ["risk-cache"], ); const getUsers = unstable_cache( - async (organizationId: string) => { - const users = await db.user.findMany({ - where: { organizationId: organizationId }, - }); + async (organizationId: string) => { + const users = await db.user.findMany({ + where: { organizationId: organizationId }, + }); - return users; - }, - ["users-cache"], + return users; + }, + ["users-cache"], ); export async function generateMetadata({ - params, + params, }: { - params: Promise<{ locale: string }>; + params: Promise<{ locale: string }>; }): Promise { - const { locale } = await params; - setStaticParamsLocale(locale); - const t = await getI18n(); + const { locale } = await params; + setStaticParamsLocale(locale); + const t = await getI18n(); - return { - title: t("sub_pages.risk.tasks.task_overview"), - }; + return { + title: t("sub_pages.risk.tasks.task_overview"), + }; } diff --git a/apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/tests/(overview)/page.tsx b/apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/tests/(overview)/page.tsx index aa7a3a6696..fe29230385 100644 --- a/apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/tests/(overview)/page.tsx +++ b/apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/tests/(overview)/page.tsx @@ -9,113 +9,113 @@ import { unstable_cache } from "next/cache"; import { redirect } from "next/navigation"; export default async function TestsOverview({ - params, + params, }: { - params: Promise<{ locale: string }>; + params: Promise<{ locale: string }>; }) { - const { locale } = await params; - setStaticParamsLocale(locale); + const { locale } = await params; + setStaticParamsLocale(locale); - const session = await auth(); + const session = await auth(); - if (!session?.user?.organizationId) { - redirect("/onboarding"); - } + if (!session?.user?.organizationId) { + redirect("/onboarding"); + } - const overview = await getTestsOverview(session.user.organizationId); + const overview = await getTestsOverview(session.user.organizationId); - if (overview?.totalTests === 0) { - redirect("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/tests/all"); - } + if (overview?.totalTests === 0) { + redirect(`/${session.user.organizationId}/tests/all`); + } - return ( -
-
- - -
-
- ); + return ( +
+
+ + +
+
+ ); } const getTestsOverview = unstable_cache( - async (organizationId: string) => { - return await db.$transaction(async (tx) => { - const [ - totalTests, - infoSeverityTests, - lowSeverityTests, - mediumSeverityTests, - highSeverityTests, - criticalSeverityTests, - ] = await Promise.all([ - tx.organizationIntegrationResults.count({ - where: { - organizationId, - }, - }), - tx.organizationIntegrationResults.count({ - where: { - organizationId, - label: "INFO", - }, - }), - tx.organizationIntegrationResults.count({ - where: { - organizationId, - label: "LOW", - }, - }), - tx.organizationIntegrationResults.count({ - where: { - organizationId, - label: "MEDIUM", - }, - }), - tx.organizationIntegrationResults.count({ - where: { - organizationId, - label: "HIGH", - }, - }), - tx.organizationIntegrationResults.count({ - where: { - organizationId, - label: "CRITICAL", - }, - }), - ]); + async (organizationId: string) => { + return await db.$transaction(async (tx) => { + const [ + totalTests, + infoSeverityTests, + lowSeverityTests, + mediumSeverityTests, + highSeverityTests, + criticalSeverityTests, + ] = await Promise.all([ + tx.organizationIntegrationResults.count({ + where: { + organizationId, + }, + }), + tx.organizationIntegrationResults.count({ + where: { + organizationId, + label: "INFO", + }, + }), + tx.organizationIntegrationResults.count({ + where: { + organizationId, + label: "LOW", + }, + }), + tx.organizationIntegrationResults.count({ + where: { + organizationId, + label: "MEDIUM", + }, + }), + tx.organizationIntegrationResults.count({ + where: { + organizationId, + label: "HIGH", + }, + }), + tx.organizationIntegrationResults.count({ + where: { + organizationId, + label: "CRITICAL", + }, + }), + ]); - return { - totalTests, - infoSeverityTests, - lowSeverityTests, - mediumSeverityTests, - highSeverityTests, - criticalSeverityTests, - }; - }); - }, - ["tests-overview-cache"], + return { + totalTests, + infoSeverityTests, + lowSeverityTests, + mediumSeverityTests, + highSeverityTests, + criticalSeverityTests, + }; + }); + }, + ["tests-overview-cache"], ); export async function generateMetadata({ - params, + params, }: { - params: Promise<{ locale: string }>; + params: Promise<{ locale: string }>; }): Promise { - const { locale } = await params; - setStaticParamsLocale(locale); - const t = await getI18n(); + const { locale } = await params; + setStaticParamsLocale(locale); + const t = await getI18n(); - return { - title: t("sidebar.tests"), - }; + return { + title: t("sidebar.tests"), + }; } diff --git a/apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/tests/all/[testId]/actions/createTestComment.ts b/apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/tests/all/[testId]/actions/createTestComment.ts index 7b820a5ae2..346d2efea3 100644 --- a/apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/tests/all/[testId]/actions/createTestComment.ts +++ b/apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/tests/all/[testId]/actions/createTestComment.ts @@ -31,8 +31,8 @@ export const createTestCommentAction = authActionClient }, }); - revalidatePath(`/tests/${testId}`); + revalidatePath(`/${user.organizationId}/tests/${testId}`); revalidateTag(`test_${user.organizationId}`); return { success: true }; - }); \ No newline at end of file + }); diff --git a/apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/vendors/(overview)/page.tsx b/apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/vendors/(overview)/page.tsx index 9a6972eeb6..50bff17745 100644 --- a/apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/vendors/(overview)/page.tsx +++ b/apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/vendors/(overview)/page.tsx @@ -9,66 +9,66 @@ import { unstable_cache } from "next/cache"; import { redirect } from "next/navigation"; export default async function RiskManagement({ - params, + params, }: { - params: Promise<{ locale: string }>; + params: Promise<{ locale: string }>; }) { - const { locale } = await params; - setStaticParamsLocale(locale); + const { locale } = await params; + setStaticParamsLocale(locale); - const session = await auth(); + const session = await auth(); - if (!session?.user?.organizationId) { - redirect("/onboarding"); - } + if (!session?.user?.organizationId) { + redirect("/onboarding"); + } - const overview = await getRiskOverview(session.user.organizationId); + const overview = await getRiskOverview(session.user.organizationId); - if (overview?.risks === 0) { - redirect("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/risk/register"); - } + if (overview?.risks === 0) { + redirect(`/${session.user.organizationId}/risk/register`); + } - return ( -
- Coming Soon - {/*
+ return ( +
+ Coming Soon + {/*
*/} -
- ); +
+ ); } const getRiskOverview = unstable_cache( - async (organizationId: string) => { - return await db.$transaction(async (tx) => { - const [risks] = await Promise.all([ - tx.risk.count({ - where: { organizationId }, - }), - ]); + async (organizationId: string) => { + return await db.$transaction(async (tx) => { + const [risks] = await Promise.all([ + tx.risk.count({ + where: { organizationId }, + }), + ]); - return { - risks, - }; - }); - }, - ["risk-overview-cache"], + return { + risks, + }; + }); + }, + ["risk-overview-cache"], ); export async function generateMetadata({ - params, + params, }: { - params: Promise<{ locale: string }>; + params: Promise<{ locale: string }>; }): Promise { - const { locale } = await params; - setStaticParamsLocale(locale); - const t = await getI18n(); + const { locale } = await params; + setStaticParamsLocale(locale); + const t = await getI18n(); - return { - title: t("sidebar.risk"), - }; + return { + title: t("sidebar.risk"), + }; } diff --git a/apps/app/src/components/tables/risk-register/columns.tsx b/apps/app/src/components/tables/risk-register/columns.tsx index 22821c5abe..a261cc2c62 100644 --- a/apps/app/src/components/tables/risk-register/columns.tsx +++ b/apps/app/src/components/tables/risk-register/columns.tsx @@ -8,102 +8,104 @@ import { Button } from "@bubba/ui/button"; import { cn } from "@bubba/ui/cn"; import type { ColumnDef } from "@tanstack/react-table"; import Link from "next/link"; +import { useParams } from "next/navigation"; export type RiskRegisterType = { - id: string; - title: string; - status: RiskStatus; - department?: Departments; - ownerId: string; - owner: { - image: string; - name: string; - }; + id: string; + title: string; + status: RiskStatus; + department?: Departments; + ownerId: string; + owner: { + image: string; + name: string; + }; }; export function columns(): ColumnDef[] { - const t = useI18n(); + const t = useI18n(); + const { orgId } = useParams<{ orgId: string }>(); - return [ - { - id: "name", - accessorKey: "name", - header: t("risk.register.table.risk"), - cell: ({ row }) => { - const status = row.original.status; + return [ + { + id: "name", + accessorKey: "name", + header: t("risk.register.table.risk"), + cell: ({ row }) => { + const status = row.original.status; - return ( -
- -
- -
-
- ); - }, - }, - { - id: "status", - accessorKey: "status", - header: () => ( - {t("common.table.status")} - ), - cell: ({ row }) => { - const status = row.original.status; + return ( +
+ +
+ +
+
+ ); + }, + }, + { + id: "status", + accessorKey: "status", + header: () => ( + {t("common.table.status")} + ), + cell: ({ row }) => { + const status = row.original.status; - return ( -
- -
- ); - }, - }, - { - id: "department", - accessorKey: "department", - header: () => ( - - {t("common.filters.department")} - - ), - cell: ({ row }) => { - const department = row.original.department; + return ( +
+ +
+ ); + }, + }, + { + id: "department", + accessorKey: "department", + header: () => ( + + {t("common.filters.department")} + + ), + cell: ({ row }) => { + const department = row.original.department; - if (!department) { - return —; - } + if (!department) { + return —; + } - return ( - - - {department.replace(/_/g, " ").toUpperCase()} - - - ); - }, - }, - { - id: "ownerId", - accessorKey: "ownerId", - header: () => ( - - {t("common.assignee.label")} - - ), - cell: ({ row }) => { - return ( -
- -
- ); - }, - }, - ]; + return ( + + + {department.replace(/_/g, " ").toUpperCase()} + + + ); + }, + }, + { + id: "ownerId", + accessorKey: "ownerId", + header: () => ( + + {t("common.assignee.label")} + + ), + cell: ({ row }) => { + return ( +
+ +
+ ); + }, + }, + ]; } diff --git a/apps/app/src/components/tables/risk-tasks/columns.tsx b/apps/app/src/components/tables/risk-tasks/columns.tsx index 1db4633f24..b4ac9c8923 100644 --- a/apps/app/src/components/tables/risk-tasks/columns.tsx +++ b/apps/app/src/components/tables/risk-tasks/columns.tsx @@ -7,95 +7,97 @@ import type { RiskTaskStatus } from "@bubba/db/types"; import { Button } from "@bubba/ui/button"; import type { ColumnDef } from "@tanstack/react-table"; import Link from "next/link"; +import { useParams } from "next/navigation"; export type RiskTaskType = { - id: string; - riskId: string; - title: string; - status: RiskTaskStatus; - dueDate: string; - ownerId: string; - owner: { - image: string; - name: string; - }; + id: string; + riskId: string; + title: string; + status: RiskTaskStatus; + dueDate: string; + ownerId: string; + owner: { + image: string; + name: string; + }; }; export function columns(): ColumnDef[] { - const t = useI18n(); + const t = useI18n(); + const { orgId } = useParams<{ orgId: string }>(); - return [ - { - id: "title", - accessorKey: "title", - header: t("risk.tasks.title"), - cell: ({ row }) => { - return ( - - - - ); - }, - }, - { - id: "status", - accessorKey: "status", - header: t("common.table.status"), - cell: ({ row }) => { - const status = row.original.status; + return [ + { + id: "title", + accessorKey: "title", + header: t("risk.tasks.title"), + cell: ({ row }) => { + return ( + + + + ); + }, + }, + { + id: "status", + accessorKey: "status", + header: t("common.table.status"), + cell: ({ row }) => { + const status = row.original.status; - return ( -
- -
- ); - }, - }, - { - id: "dueDate", - accessorKey: "dueDate", - header: () => ( - - {t("common.table.due_date")} - - ), - cell: ({ row }) => { - const status = row.original.status; + return ( +
+ +
+ ); + }, + }, + { + id: "dueDate", + accessorKey: "dueDate", + header: () => ( + + {t("common.table.due_date")} + + ), + cell: ({ row }) => { + const status = row.original.status; - return ( -
- -
- ); - }, - }, - { - id: "ownerId", - accessorKey: "ownerId", - header: () => ( - - {t("common.table.assigned_to")} - - ), - cell: ({ row }) => { - return ( -
- -
- ); - }, - }, - ]; + return ( +
+ +
+ ); + }, + }, + { + id: "ownerId", + accessorKey: "ownerId", + header: () => ( + + {t("common.table.assigned_to")} + + ), + cell: ({ row }) => { + return ( +
+ +
+ ); + }, + }, + ]; } diff --git a/apps/app/src/components/tables/risk-tasks/empty-states.tsx b/apps/app/src/components/tables/risk-tasks/empty-states.tsx index 7f2d9d0bfb..8aefb93b56 100644 --- a/apps/app/src/components/tables/risk-tasks/empty-states.tsx +++ b/apps/app/src/components/tables/risk-tasks/empty-states.tsx @@ -10,63 +10,63 @@ import { useRouter } from "next/navigation"; import { useQueryState } from "nuqs"; type Props = { - hasFilters?: boolean; + hasFilters?: boolean; }; export function NoResults({ hasFilters }: Props) { - const t = useI18n(); - const router = useRouter(); - const params = useParams<{ riskId: string }>(); + const t = useI18n(); + const router = useRouter(); + const { orgId, riskId } = useParams<{ orgId: string; riskId: string }>(); - return ( -
-
- -
-

- {t("common.empty_states.no_results.title")} -

-

- {hasFilters - ? t("common.empty_states.no_results.description_filters") - : t("common.empty_states.no_results.description_no_tasks")} -

-
+ return ( +
+
+ +
+

+ {t("common.empty_states.no_results.title")} +

+

+ {hasFilters + ? t("common.empty_states.no_results.description_filters") + : t("common.empty_states.no_results.description_no_tasks")} +

+
- {hasFilters && ( - - )} -
-
- ); + {hasFilters && ( + + )} +
+
+ ); } export function NoTasks({ isEmpty }: { isEmpty: boolean }) { - const t = useI18n(); + const t = useI18n(); - const [open, setOpen] = useQueryState("create-task-sheet"); + const [open, setOpen] = useQueryState("create-task-sheet"); - return ( -
-
-

- {t("common.empty_states.no_results.title_tasks")} -

-

- {t("common.empty_states.no_results.description_no_tasks")} -

- -
+ return ( +
+
+

+ {t("common.empty_states.no_results.title_tasks")} +

+

+ {t("common.empty_states.no_results.description_no_tasks")} +

+ +
- -
- ); + +
+ ); } diff --git a/apps/app/src/components/tables/tests/empty-states.tsx b/apps/app/src/components/tables/tests/empty-states.tsx index 98b1e2d252..13944be7d1 100644 --- a/apps/app/src/components/tables/tests/empty-states.tsx +++ b/apps/app/src/components/tables/tests/empty-states.tsx @@ -4,7 +4,7 @@ import { EmployeeInviteSheet } from "@/components/sheets/invite-user-sheet"; import { useI18n } from "@/locales/client"; import { Button } from "@bubba/ui/button"; import { CloudOff } from "lucide-react"; -import { useRouter } from "next/navigation"; +import { useParams, useRouter } from "next/navigation"; interface Props { hasFilters?: boolean; @@ -13,6 +13,7 @@ interface Props { export function NoResults({ hasFilters }: Props) { const router = useRouter(); const t = useI18n(); + const { orgId } = useParams<{ orgId: string }>(); return (
@@ -30,7 +31,10 @@ export function NoResults({ hasFilters }: Props) {
{hasFilters && ( - )} diff --git a/apps/app/src/jobs/tasks/notifications/risk-task-notification.ts b/apps/app/src/jobs/tasks/notifications/risk-task-notification.ts index 62726611ce..a20dddc4a8 100644 --- a/apps/app/src/jobs/tasks/notifications/risk-task-notification.ts +++ b/apps/app/src/jobs/tasks/notifications/risk-task-notification.ts @@ -49,7 +49,7 @@ export const sendRiskTaskNotification = schemaTask({ }, payload: { description: `${task.title} is due ${timeUntilDue}`, - recordId: `/risk/${task.riskId}/tasks/${task.id}`, + recordId: `/${owner.organizationId}/risk/${task.riskId}/tasks/${task.id}`, type: NotificationTypes.Task, }, });