diff --git a/apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/evidence/overview/data/getEvidenceDashboard.ts b/apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/evidence/overview/data/getEvidenceDashboard.ts index 7750434196..4c7a7d26a7 100644 --- a/apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/evidence/overview/data/getEvidenceDashboard.ts +++ b/apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/evidence/overview/data/getEvidenceDashboard.ts @@ -1,6 +1,5 @@ import type { Departments, OrganizationEvidence } from "@bubba/db/types"; import { db } from "@bubba/db"; -import { cache } from "react"; export type EvidenceStatus = "empty" | "draft" | "needsReview" | "upToDate"; @@ -29,190 +28,190 @@ export interface EvidenceDashboardData { * @param organizationId - The ID of the organization to get the evidence for * @returns The evidence dashboard data for the given organization */ -export const getEvidenceDashboard = cache( - async (organizationId: string): Promise => { - const evidence = await db.organizationEvidence.findMany({ - where: { - organizationId, - }, - include: { - assignee: true, - }, - }); - - if (!evidence) { - return null; - } +export const getEvidenceDashboard = async ( + organizationId: string +): Promise => { + const evidence = await db.organizationEvidence.findMany({ + where: { + organizationId, + }, + include: { + assignee: true, + }, + }); + + if (!evidence) { + return null; + } - // Calculate status for each evidence item - const now = new Date(); - let emptyCount = 0; - let draftCount = 0; - let needsReviewCount = 0; - let upToDateCount = 0; - const totalCount = evidence.length; - - const evidenceWithStatus: EvidenceWithStatus[] = evidence.map((item) => { - // Check if task has files or links - const hasContent = - item.fileUrls.length > 0 || item.additionalUrls.length > 0; - - // Check if task is published - const isPublished = item.published; - - // Check if task needs review (published and next review date is in the past) - let nextReviewDate = null; - - if (item.lastPublishedAt && item.frequency) { - // Calculate next review date based on last published date and frequency - const lastPublished = new Date(item.lastPublishedAt); - nextReviewDate = new Date(lastPublished); - - switch (item.frequency) { - case "monthly": - nextReviewDate.setMonth(nextReviewDate.getMonth() + 1); - break; - case "quarterly": - nextReviewDate.setMonth(nextReviewDate.getMonth() + 3); - break; - case "yearly": - nextReviewDate.setFullYear(nextReviewDate.getFullYear() + 1); - break; - } + // Calculate status for each evidence item + const now = new Date(); + let emptyCount = 0; + let draftCount = 0; + let needsReviewCount = 0; + let upToDateCount = 0; + const totalCount = evidence.length; + + const evidenceWithStatus: EvidenceWithStatus[] = evidence.map((item) => { + // Check if task has files or links + const hasContent = + item.fileUrls.length > 0 || item.additionalUrls.length > 0; + + // Check if task is published + const isPublished = item.published; + + // Check if task needs review (published and next review date is in the past) + let nextReviewDate = null; + + if (item.lastPublishedAt && item.frequency) { + // Calculate next review date based on last published date and frequency + const lastPublished = new Date(item.lastPublishedAt); + nextReviewDate = new Date(lastPublished); + + switch (item.frequency) { + case "monthly": + nextReviewDate.setMonth(nextReviewDate.getMonth() + 1); + break; + case "quarterly": + nextReviewDate.setMonth(nextReviewDate.getMonth() + 3); + break; + case "yearly": + nextReviewDate.setFullYear(nextReviewDate.getFullYear() + 1); + break; } + } - const isPastDue = nextReviewDate && nextReviewDate < now; - - let status: EvidenceStatus; - - if (!hasContent && !isPublished) { - // No files or links - status = "empty"; - emptyCount++; - } else if (!isPublished) { - // Has content but not published - status = "draft"; - draftCount++; - } else if (isPastDue) { - // Published but needs review - status = "needsReview"; - needsReviewCount++; - } else { - // Published and up to date - status = "upToDate"; - upToDateCount++; - } + const isPastDue = nextReviewDate && nextReviewDate < now; + + let status: EvidenceStatus; + + if (!hasContent && !isPublished) { + // No files or links + status = "empty"; + emptyCount++; + } else if (!isPublished) { + // Has content but not published + status = "draft"; + draftCount++; + } else if (isPastDue) { + // Published but needs review + status = "needsReview"; + needsReviewCount++; + } else { + // Published and up to date + status = "upToDate"; + upToDateCount++; + } - // Extract assignee email if available - const assigneeEmail = item.assignee?.email || undefined; - - // Create a new object without the assignee property - const { assignee, ...evidenceWithoutAssignee } = item; - - return { - ...evidenceWithoutAssignee, - status, - assigneeEmail, - }; - }); - - // Initialize with all department values from the enum - const departmentValues: Departments[] = [ - "none", - "admin", - "gov", - "hr", - "it", - "itsm", - "qms", - ]; - - // Initialize the byDepartment object with empty arrays for all departments - const initialByDepartment = departmentValues.reduce< - Record - >( - (acc, dept) => { - acc[dept] = []; - return acc; - }, - {} as Record - ); - - // Group evidence by department - const byDepartment = evidenceWithStatus.reduce< - Record - >((acc, curr) => { - // Use the department from the evidence, or default to 'none' if it's null/undefined - const department = curr.department || "none"; - - // Add the evidence to the appropriate department array - acc[department].push(curr); - return acc; - }, initialByDepartment); + // Extract assignee email if available + const assigneeEmail = item.assignee?.email || undefined; - // Collect unassigned evidence - const unassigned = evidenceWithStatus.filter((item) => !item.assigneeId); + // Create a new object without the assignee property + const { assignee, ...evidenceWithoutAssignee } = item; - // Group evidence by assignee - const byAssignee = evidenceWithStatus.reduce< - Record - >((acc, curr) => { - if (!curr.assigneeId || !curr.assigneeEmail) { - return acc; - } - - const email = curr.assigneeEmail; - if (!acc[email]) { - acc[email] = []; - } - acc[email].push(curr); + return { + ...evidenceWithoutAssignee, + status, + assigneeEmail, + }; + }); + + // Initialize with all department values from the enum + const departmentValues: Departments[] = [ + "none", + "admin", + "gov", + "hr", + "it", + "itsm", + "qms", + ]; + + // Initialize the byDepartment object with empty arrays for all departments + const initialByDepartment = departmentValues.reduce< + Record + >( + (acc, dept) => { + acc[dept] = []; + return acc; + }, + {} as Record + ); + + // Group evidence by department + const byDepartment = evidenceWithStatus.reduce< + Record + >((acc, curr) => { + // Use the department from the evidence, or default to 'none' if it's null/undefined + const department = curr.department || "none"; + + // Add the evidence to the appropriate department array + acc[department].push(curr); + return acc; + }, initialByDepartment); + + // Collect unassigned evidence + const unassigned = evidenceWithStatus.filter((item) => !item.assigneeId); + + // Group evidence by assignee + const byAssignee = evidenceWithStatus.reduce< + Record + >((acc, curr) => { + if (!curr.assigneeId || !curr.assigneeEmail) { return acc; - }, {}); - - // Sample the first assignee's data - const firstAssigneeEmail = Object.keys(byAssignee)[0]; - if (firstAssigneeEmail) { - const firstAssigneeItems = byAssignee[firstAssigneeEmail]; - // Count items by status for the first assignee - const statusCounts = { - empty: 0, - draft: 0, - needsReview: 0, - upToDate: 0, - }; - - for (const item of firstAssigneeItems) { - statusCounts[item.status]++; - } } - const byFramework = evidenceWithStatus.reduce< - Record - >((acc, curr) => { - const frameworkId = curr.frameworkId; - if (!frameworkId) { - return acc; - } + const email = curr.assigneeEmail; + if (!acc[email]) { + acc[email] = []; + } + acc[email].push(curr); + return acc; + }, {}); + + // Sample the first assignee's data + const firstAssigneeEmail = Object.keys(byAssignee)[0]; + if (firstAssigneeEmail) { + const firstAssigneeItems = byAssignee[firstAssigneeEmail]; + // Count items by status for the first assignee + const statusCounts = { + empty: 0, + draft: 0, + needsReview: 0, + upToDate: 0, + }; - if (!acc[frameworkId]) { - acc[frameworkId] = []; - } - acc[frameworkId].push(curr); + for (const item of firstAssigneeItems) { + statusCounts[item.status]++; + } + } + + const byFramework = evidenceWithStatus.reduce< + Record + >((acc, curr) => { + const frameworkId = curr.frameworkId; + if (!frameworkId) { return acc; - }, {}); + } - return { - byDepartment, - byAssignee, - byFramework, - unassigned, - statusCounts: { - totalCount, - emptyCount, - draftCount, - needsReviewCount, - upToDateCount, - }, - }; - } -); + if (!acc[frameworkId]) { + acc[frameworkId] = []; + } + acc[frameworkId].push(curr); + return acc; + }, {}); + + return { + byDepartment, + byAssignee, + byFramework, + unassigned, + statusCounts: { + totalCount, + emptyCount, + draftCount, + needsReviewCount, + upToDateCount, + }, + }; +}; diff --git a/apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/policies/(overview)/page.tsx b/apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/policies/(overview)/page.tsx index bac5ce963d..6b84571073 100644 --- a/apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/policies/(overview)/page.tsx +++ b/apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/policies/(overview)/page.tsx @@ -3,160 +3,164 @@ import { getI18n } from "@/locales/server"; import { db } from "@bubba/db"; import type { Metadata } from "next"; import { setStaticParamsLocale } from "next-international/server"; -import { cache, Suspense } from "react"; +import { Suspense } from "react"; import { PolicyStatusChart } from "./components/policy-status-chart"; import { PolicyAssigneeChart } from "./components/policy-assignee-chart"; export default async function PoliciesOverview({ - params, + params, }: { - params: Promise<{ locale: string }>; + params: Promise<{ locale: string }>; }) { - const { locale } = await params; - setStaticParamsLocale(locale); - - const overview = await getPoliciesOverview(); - - return ( - -
-
- - -
-
-
- ); + const { locale } = await params; + setStaticParamsLocale(locale); + + const overview = await getPoliciesOverview(); + + return ( + +
+
+ + +
+
+
+ ); } -const getPoliciesOverview = cache(async () => { - const session = await auth(); - - if (!session?.user?.organizationId) { - return null; - } - - const organizationId = session.user.organizationId; - - return await db.$transaction(async (tx) => { - const [ - totalPolicies, - publishedPolicies, - draftPolicies, - archivedPolicies, - needsReviewPolicies, - policiesByAssignee, - policiesByAssigneeStatus - ] = await Promise.all([ - tx.organizationPolicy.count({ - where: { - organizationId, - }, - }), - tx.organizationPolicy.count({ - where: { - organizationId, - status: "published", - }, - }), - tx.organizationPolicy.count({ - where: { - organizationId, - status: "draft", - }, - }), - tx.organizationPolicy.count({ - where: { - organizationId, - status: "archived", - }, - }), - tx.organizationPolicy.count({ - where: { - organizationId, - status: "needs_review", - }, - }), - tx.organizationPolicy.groupBy({ - by: ["ownerId"], - _count: true, - where: { - organizationId, - ownerId: { not: null }, - }, - }), - tx.organizationPolicy.findMany({ - where: { - organizationId, - ownerId: { not: null }, - }, - select: { - status: true, - owner: { - select: { - id: true, - name: true, - }, - }, - }, - }), - ]); - - // Transform the data for easier consumption by the chart component - // First group by owner - const policyDataByOwner = new Map(); - - for (const policy of policiesByAssigneeStatus) { - if (!policy.owner) continue; - - const ownerId = policy.owner.id; - if (!policyDataByOwner.has(ownerId)) { - policyDataByOwner.set(ownerId, { - id: ownerId, - name: policy.owner.name || 'Unknown', - total: 0, - published: 0, - draft: 0, - archived: 0, - needs_review: 0, - }); - } - - const ownerData = policyDataByOwner.get(ownerId); - ownerData.total += 1; - - // Handle each status type explicitly - const status = policy.status as 'published' | 'draft' | 'archived' | 'needs_review'; - if (status === 'published') ownerData.published += 1; - else if (status === 'draft') ownerData.draft += 1; - else if (status === 'archived') ownerData.archived += 1; - else if (status === 'needs_review') ownerData.needs_review += 1; - } - - const assigneeData = Array.from(policyDataByOwner.values()); - - return { - totalPolicies, - publishedPolicies, - draftPolicies, - archivedPolicies, - needsReviewPolicies, - policiesByAssignee, - assigneeData, - }; - }); -}); +const getPoliciesOverview = async () => { + const session = await auth(); + + if (!session?.user?.organizationId) { + return null; + } + + const organizationId = session.user.organizationId; + + return await db.$transaction(async (tx) => { + const [ + totalPolicies, + publishedPolicies, + draftPolicies, + archivedPolicies, + needsReviewPolicies, + policiesByAssignee, + policiesByAssigneeStatus, + ] = await Promise.all([ + tx.organizationPolicy.count({ + where: { + organizationId, + }, + }), + tx.organizationPolicy.count({ + where: { + organizationId, + status: "published", + }, + }), + tx.organizationPolicy.count({ + where: { + organizationId, + status: "draft", + }, + }), + tx.organizationPolicy.count({ + where: { + organizationId, + status: "archived", + }, + }), + tx.organizationPolicy.count({ + where: { + organizationId, + status: "needs_review", + }, + }), + tx.organizationPolicy.groupBy({ + by: ["ownerId"], + _count: true, + where: { + organizationId, + ownerId: { not: null }, + }, + }), + tx.organizationPolicy.findMany({ + where: { + organizationId, + ownerId: { not: null }, + }, + select: { + status: true, + owner: { + select: { + id: true, + name: true, + }, + }, + }, + }), + ]); + + // Transform the data for easier consumption by the chart component + // First group by owner + const policyDataByOwner = new Map(); + + for (const policy of policiesByAssigneeStatus) { + if (!policy.owner) continue; + + const ownerId = policy.owner.id; + if (!policyDataByOwner.has(ownerId)) { + policyDataByOwner.set(ownerId, { + id: ownerId, + name: policy.owner.name || "Unknown", + total: 0, + published: 0, + draft: 0, + archived: 0, + needs_review: 0, + }); + } + + const ownerData = policyDataByOwner.get(ownerId); + ownerData.total += 1; + + // Handle each status type explicitly + const status = policy.status as + | "published" + | "draft" + | "archived" + | "needs_review"; + if (status === "published") ownerData.published += 1; + else if (status === "draft") ownerData.draft += 1; + else if (status === "archived") ownerData.archived += 1; + else if (status === "needs_review") ownerData.needs_review += 1; + } + + const assigneeData = Array.from(policyDataByOwner.values()); + + return { + totalPolicies, + publishedPolicies, + draftPolicies, + archivedPolicies, + needsReviewPolicies, + policiesByAssignee, + assigneeData, + }; + }); +}; 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.policies"), - }; + return { + title: t("sidebar.policies"), + }; } diff --git a/apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/policies/all/(overview)/page.tsx b/apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/policies/all/(overview)/page.tsx index 2cbd764fe3..4979f06f2c 100644 --- a/apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/policies/all/(overview)/page.tsx +++ b/apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/policies/all/(overview)/page.tsx @@ -6,62 +6,55 @@ import { setStaticParamsLocale } from "next-international/server"; import { redirect } from "next/navigation"; import { PoliciesList } from "./components/PoliciesList"; import { db } from "@bubba/db"; -import { unstable_cache } from "next/cache"; export default async function PoliciesPage({ - 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 organizationId = session?.user.organizationId; + const session = await auth(); + const organizationId = session?.user.organizationId; - if (!organizationId) { - return redirect("/"); - } + if (!organizationId) { + return redirect("/"); + } - const users = await getUsers(organizationId); + const users = await getUsers(organizationId); - const columnHeaders = await getServerColumnHeaders(); + const columnHeaders = await getServerColumnHeaders(); - return ; + return ; } export async function generateMetadata({ - params, + params, }: { - params: Promise<{ locale: string }>; + params: Promise<{ locale: string }>; }): Promise { - const { locale } = await params; + const { locale } = await params; - setStaticParamsLocale(locale); - const t = await getI18n(); + setStaticParamsLocale(locale); + const t = await getI18n(); - return { - title: t("sidebar.policies"), - }; + return { + title: t("sidebar.policies"), + }; } -const getUsers = unstable_cache( - async (organizationId: string) => { - const users = await db.user.findMany({ - where: { - organizationId, - }, - select: { - id: true, - name: true, - image: true, - }, - }); - - return users; - }, - ["organization-users"], - { - tags: ["organization-users"], - }, -); +const getUsers = async (organizationId: string) => { + const users = await db.user.findMany({ + where: { + organizationId, + }, + select: { + id: true, + name: true, + image: true, + }, + }); + + return users; +}; diff --git a/apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/policies/all/[policyId]/layout.tsx b/apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/policies/all/[policyId]/layout.tsx index 90e98cfa72..51e74bdc32 100644 --- a/apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/policies/all/[policyId]/layout.tsx +++ b/apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/policies/all/[policyId]/layout.tsx @@ -3,7 +3,6 @@ import { getI18n } from "@/locales/server"; import { db } from "@bubba/db"; import { SecondaryMenu } from "@bubba/ui/secondary-menu"; import { redirect } from "next/navigation"; -import { cache } from "react"; interface LayoutProps { children: React.ReactNode; @@ -49,7 +48,7 @@ export default async function Layout({ children, params }: LayoutProps) { ); } -const getPolicy = cache(async (policyId: string, organizationId: string) => { +const getPolicy = async (policyId: string, organizationId: string) => { const policy = await db.organizationPolicy.findUnique({ where: { id: policyId, @@ -61,4 +60,4 @@ const getPolicy = cache(async (policyId: string, organizationId: string) => { }); return policy; -}); +}; diff --git a/apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/policies/all/[policyId]/page.tsx b/apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/policies/all/[policyId]/page.tsx index f07a4063d4..e6907db1a3 100644 --- a/apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/policies/all/[policyId]/page.tsx +++ b/apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/policies/all/[policyId]/page.tsx @@ -5,7 +5,6 @@ import { db } from "@bubba/db"; import type { Metadata } from "next"; import { setStaticParamsLocale } from "next-international/server"; import { redirect } from "next/navigation"; -import { cache } from "react"; export default async function PolicyDetails({ params, @@ -45,7 +44,7 @@ export async function generateMetadata({ }; } -const getPolicy = cache(async (policyId: string, organizationId: string) => { +const getPolicy = async (policyId: string, organizationId: string) => { const policy = await db.organizationPolicy.findUnique({ where: { id: policyId, organizationId }, include: { @@ -54,7 +53,7 @@ const getPolicy = cache(async (policyId: string, organizationId: string) => { }, }); return policy; -}); +}; const getUsers = async (organizationId: string) => { const orgMembers = await db.organizationMember.findMany({ diff --git a/apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/risk/(overview)/page.tsx b/apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/risk/(overview)/page.tsx index 16c79a8a47..2f79996eca 100644 --- a/apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/risk/(overview)/page.tsx +++ b/apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/risk/(overview)/page.tsx @@ -5,7 +5,6 @@ import { getI18n } from "@/locales/server"; import { db } from "@bubba/db"; import type { Metadata } from "next"; import { setStaticParamsLocale } from "next-international/server"; -import { unstable_cache } from "next/cache"; import { redirect } from "next/navigation"; export default async function RiskManagement({ @@ -42,22 +41,19 @@ export default async function RiskManagement({ ); } -const getRiskOverview = unstable_cache( - async (organizationId: string) => { - return await db.$transaction(async (tx) => { - const [risks] = await Promise.all([ - tx.risk.count({ - where: { organizationId }, - }), - ]); +const getRiskOverview = 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, + }; + }); +}; export async function generateMetadata({ params, 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 27a5060628..70121fc8e4 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 @@ -4,7 +4,6 @@ import { getI18n } from "@/locales/server"; import { db } from "@bubba/db"; import type { Metadata } from "next"; import { setStaticParamsLocale } from "next-international/server"; -import { unstable_cache } from "next/cache"; import { redirect } from "next/navigation"; interface PageProps { @@ -38,38 +37,32 @@ export default async function RiskPage({ params }: PageProps) { ); } -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", - }, +const getRisk = 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; +}; -const getUsers = unstable_cache( - async (organizationId: string) => { - const users = await db.user.findMany({ - where: { organizationId: organizationId }, - }); +const getUsers = async (organizationId: string) => { + const users = await db.user.findMany({ + where: { organizationId: organizationId }, + }); - return users; - }, - ["users-cache"], -); + return users; +}; export async function generateMetadata({ params, 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 f85b4ce8cc..04d6eddd17 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 @@ -1,9 +1,7 @@ import { auth } from "@/auth"; -import { Title } from "@/components/title"; import { getI18n } from "@/locales/server"; import { db } from "@bubba/db"; import { SecondaryMenu } from "@bubba/ui/secondary-menu"; -import { unstable_cache } from "next/cache"; import { redirect } from "next/navigation"; interface LayoutProps { @@ -50,16 +48,13 @@ export default async function Layout({ children, params }: LayoutProps) { ); } -const getRisk = unstable_cache( - async (riskId: string, organizationId: string) => { - const risk = await db.risk.findUnique({ - where: { - id: riskId, - organizationId: organizationId, - }, - }); +const getRisk = async (riskId: string, organizationId: string) => { + const risk = await db.risk.findUnique({ + where: { + id: riskId, + organizationId: organizationId, + }, + }); - return risk; - }, - ["risk-cache"], -); + return risk; +}; 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 8c558ec1ad..e28293bbd5 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 @@ -17,7 +17,6 @@ import type { RiskTaskStatus } from "@bubba/db/types"; import { Card, CardContent, CardHeader, CardTitle } from "@bubba/ui/card"; import type { Metadata } from "next"; import { setStaticParamsLocale } from "next-international/server"; -import { unstable_cache } from "next/cache"; import { redirect } from "next/navigation"; interface PageProps { @@ -115,94 +114,42 @@ export default async function RiskPage({ searchParams, params }: PageProps) { ); } -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"], -); +const getRisk = async (riskId: string, organizationId: string) => { + const risk = await db.risk.findUnique({ + where: { + id: riskId, + organizationId: organizationId, + }, + include: { + owner: true, + }, + }); -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({ + return risk; +}; + +const getTasks = 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: [ @@ -210,31 +157,74 @@ const getTasks = unstable_cache( ? { OR: [ { title: { contains: search, mode: "insensitive" } }, - { description: { 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"], -); + 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 }; +}; + +const getUsers = async (organizationId: string) => { + const users = await db.user.findMany({ + where: { organizationId: organizationId }, + }); + + return users; +}; export async function generateMetadata({ params, 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 fca0fbf411..d4684da3b5 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 @@ -6,7 +6,6 @@ import { getI18n } from "@/locales/server"; import { db } from "@bubba/db"; import type { Metadata } from "next"; import { setStaticParamsLocale } from "next-international/server"; -import { unstable_cache } from "next/cache"; import { redirect } from "next/navigation"; interface PageProps { @@ -42,35 +41,29 @@ export default async function RiskPage({ params }: PageProps) { ); } -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, - }, - }); +const getTask = 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; +}; -const getUsers = unstable_cache( - async (organizationId: string) => { - const users = await db.user.findMany({ - where: { organizationId: organizationId }, - }); +const getUsers = async (organizationId: string) => { + const users = await db.user.findMany({ + where: { organizationId: organizationId }, + }); - return users; - }, - ["users-cache"], -); + return users; +}; export async function generateMetadata({ params, diff --git a/apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/settings/api-keys/page.tsx b/apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/settings/api-keys/page.tsx index 22ba106fcf..a7d01d51b1 100644 --- a/apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/settings/api-keys/page.tsx +++ b/apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/settings/api-keys/page.tsx @@ -1,73 +1,71 @@ -import { ApiKeysTable } from "@/components/tables/api-keys"; import { auth } from "@/auth"; +import { ApiKeysTable } from "@/components/tables/api-keys"; import { getI18n } from "@/locales/server"; +import { db } from "@bubba/db"; import type { Metadata } from "next"; import { setStaticParamsLocale } from "next-international/server"; -import { redirect } from "next/navigation"; -import { db } from "@bubba/db"; -import { cache } from "react"; export default async function ApiKeysPage({ - params, + params, }: { - params: Promise<{ locale: string }>; + params: Promise<{ locale: string }>; }) { - const { locale } = await params; - setStaticParamsLocale(locale); - const t = await getI18n(); + const { locale } = await params; + setStaticParamsLocale(locale); + const t = await getI18n(); - const apiKeys = await getApiKeys(); + const apiKeys = await getApiKeys(); - return ( -
- -
- ); + return ( +
+ +
+ ); } 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("settings.api_keys.title"), - }; + return { + title: t("settings.api_keys.title"), + }; } -const getApiKeys = cache(async () => { - const session = await auth(); +const getApiKeys = async () => { + const session = await auth(); - if (!session?.user.organizationId) { - return []; - } + if (!session?.user.organizationId) { + return []; + } - const apiKeys = await db.organizationApiKey.findMany({ - where: { - organizationId: session.user.organizationId, - isActive: true, - }, - select: { - id: true, - name: true, - createdAt: true, - expiresAt: true, - lastUsedAt: true, - isActive: true, - }, - orderBy: { - createdAt: "desc", - }, - }); + const apiKeys = await db.organizationApiKey.findMany({ + where: { + organizationId: session.user.organizationId, + isActive: true, + }, + select: { + id: true, + name: true, + createdAt: true, + expiresAt: true, + lastUsedAt: true, + isActive: true, + }, + orderBy: { + createdAt: "desc", + }, + }); - return apiKeys.map((key) => ({ - ...key, - createdAt: key.createdAt.toISOString(), - expiresAt: key.expiresAt ? key.expiresAt.toISOString() : null, - lastUsedAt: key.lastUsedAt ? key.lastUsedAt.toISOString() : null, - })); -}); + return apiKeys.map((key) => ({ + ...key, + createdAt: key.createdAt.toISOString(), + expiresAt: key.expiresAt ? key.expiresAt.toISOString() : null, + lastUsedAt: key.lastUsedAt ? key.lastUsedAt.toISOString() : null, + })); +}; diff --git a/apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/settings/page.tsx b/apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/settings/page.tsx index 12f922038b..066e5ea851 100644 --- a/apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/settings/page.tsx +++ b/apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/settings/page.tsx @@ -7,58 +7,60 @@ import { db } from "@bubba/db"; import type { Metadata } from "next"; import { setStaticParamsLocale } from "next-international/server"; import { redirect } from "next/navigation"; -import { cache, Suspense } from "react"; +import { Suspense } from "react"; export default async function OrganizationSettings({ - 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) { - return redirect("/"); - } + if (!session?.user.organizationId) { + return redirect("/"); + } - const organization = await organizationDetails(session.user.organizationId); + const organization = await organizationDetails(session.user.organizationId); - return ( - -
- - - -
-
- ); + return ( + +
+ + + +
+
+ ); } 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.settings"), - }; + return { + title: t("sidebar.settings"), + }; } -const organizationDetails = cache(async (organizationId: string) => { - const organization = await db.organization.findUnique({ - where: { id: organizationId }, - select: { - name: true, - website: true, - id: true, - }, - }); +const organizationDetails = async (organizationId: string) => { + const organization = await db.organization.findUnique({ + where: { id: organizationId }, + select: { + name: true, + website: true, + id: true, + }, + }); - return organization; -}); + return organization; +}; 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 fe29230385..486971e08f 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 @@ -5,7 +5,6 @@ import { getI18n } from "@/locales/server"; import { db } from "@bubba/db"; import type { Metadata } from "next"; import { setStaticParamsLocale } from "next-international/server"; -import { unstable_cache } from "next/cache"; import { redirect } from "next/navigation"; export default async function TestsOverview({ @@ -45,66 +44,63 @@ export default async function TestsOverview({ ); } -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", - }, - }), - ]); +const getTestsOverview = 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, + }; + }); +}; export async function generateMetadata({ params, diff --git a/apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/tests/all/[testId]/page.tsx b/apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/tests/all/[testId]/page.tsx index 836bb67376..b463c34b94 100644 --- a/apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/tests/all/[testId]/page.tsx +++ b/apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/tests/all/[testId]/page.tsx @@ -5,51 +5,46 @@ import { setStaticParamsLocale } from "next-international/server"; import { redirect } from "next/navigation"; import { TestDetails } from "./components/TestDetails"; import { db } from "@bubba/db"; -import { unstable_cache } from "next/cache"; export default async function TestDetailsPage({ - params, + params, }: { - params: Promise<{ locale: string; testId: string }>; + params: Promise<{ locale: string; testId: string }>; }) { - const { locale, testId } = await params; - setStaticParamsLocale(locale); + const { locale, testId } = await params; + setStaticParamsLocale(locale); - const session = await auth(); - const organizationId = session?.user.organizationId; + const session = await auth(); + const organizationId = session?.user.organizationId; - if (!organizationId) { - redirect("/"); - } + if (!organizationId) { + redirect("/"); + } - const users = await getUsers(organizationId); + const users = await getUsers(organizationId); - return ; + return ; } export async function generateMetadata({ - params, + params, }: { - params: Promise<{ locale: string; testId: string }>; + params: Promise<{ locale: string; testId: string }>; }): Promise { - const { locale } = await params; + const { locale } = await params; - setStaticParamsLocale(locale); - const t = await getI18n(); + setStaticParamsLocale(locale); + const t = await getI18n(); - return { - title: t("sub_pages.tests.test_details"), - }; + return { + title: t("sub_pages.tests.test_details"), + }; } +const getUsers = async (organizationId: string) => { + const users = await db.user.findMany({ + where: { organizationId: organizationId }, + }); -const getUsers = unstable_cache( - async (organizationId: string) => { - const users = await db.user.findMany({ - where: { organizationId: organizationId }, - }); - - return users; - }, - ["users-cache"], -); + return users; +}; 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 50bff17745..a90ee29792 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 @@ -1,11 +1,8 @@ import { auth } from "@/auth"; -import { RiskOverview } from "@/components/risks/charts/risk-overview"; -import { RisksAssignee } from "@/components/risks/charts/risks-assignee"; import { getI18n } from "@/locales/server"; import { db } from "@bubba/db"; import type { Metadata } from "next"; import { setStaticParamsLocale } from "next-international/server"; -import { unstable_cache } from "next/cache"; import { redirect } from "next/navigation"; export default async function RiskManagement({ @@ -42,22 +39,19 @@ export default async function RiskManagement({ ); } -const getRiskOverview = unstable_cache( - async (organizationId: string) => { - return await db.$transaction(async (tx) => { - const [risks] = await Promise.all([ - tx.risk.count({ - where: { organizationId }, - }), - ]); +const getRiskOverview = 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, + }; + }); +}; export async function generateMetadata({ params, diff --git a/apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/vendors/[riskId]/comments/page.tsx b/apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/vendors/[riskId]/comments/page.tsx index b1470f9224..02e7b3584f 100644 --- a/apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/vendors/[riskId]/comments/page.tsx +++ b/apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/vendors/[riskId]/comments/page.tsx @@ -4,83 +4,76 @@ import { getI18n } from "@/locales/server"; import { db } from "@bubba/db"; import type { Metadata } from "next"; import { setStaticParamsLocale } from "next-international/server"; -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("/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", - }, - }, - }, - }); +const getRisk = 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; +}; -const getUsers = unstable_cache( - async (organizationId: string) => { - const users = await db.user.findMany({ - where: { organizationId: organizationId }, - }); +const getUsers = async (organizationId: string) => { + const users = await db.user.findMany({ + where: { organizationId: organizationId }, + }); - return users; - }, - ["users-cache"], -); + return users; +}; 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]/vendors/[riskId]/layout.tsx b/apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/vendors/[riskId]/layout.tsx index 7c74189739..a510061da6 100644 --- a/apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/vendors/[riskId]/layout.tsx +++ b/apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/vendors/[riskId]/layout.tsx @@ -1,60 +1,55 @@ import { auth } from "@/auth"; -import { Title } from "@/components/title"; import { getI18n } from "@/locales/server"; import { db } from "@bubba/db"; import { SecondaryMenu } from "@bubba/ui/secondary-menu"; -import { unstable_cache } from "next/cache"; import { redirect } from "next/navigation"; interface LayoutProps { - children: React.ReactNode; - params: Promise<{ riskId: string }>; + children: React.ReactNode; + params: Promise<{ riskId: string }>; } export default async function Layout({ children, params }: LayoutProps) { - const t = await getI18n(); - const session = await auth(); - const user = session?.user; - const orgId = user?.organizationId; - - if (!session || !orgId) { - redirect("/"); - } - - const riskId = await params; - const risk = await getRisk(riskId.riskId, orgId); - - if (!risk) { - redirect("/risk"); - } - - return ( -
- - -
{children}
-
- ); + const t = await getI18n(); + const session = await auth(); + const user = session?.user; + const orgId = user?.organizationId; + + if (!session || !orgId) { + redirect("/"); + } + + const riskId = await params; + const risk = await getRisk(riskId.riskId, orgId); + + if (!risk) { + redirect("/risk"); + } + + return ( +
+ + +
{children}
+
+ ); } -const getRisk = unstable_cache( - async (riskId: string, organizationId: string) => { - const risk = await db.risk.findUnique({ - where: { - id: riskId, - organizationId: organizationId, - }, - }); - - return risk; - }, - ["risk-cache"], -); +const getRisk = async (riskId: string, organizationId: string) => { + const risk = await db.risk.findUnique({ + where: { + id: riskId, + organizationId: organizationId, + }, + }); + + return risk; +}; diff --git a/apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/vendors/[riskId]/page.tsx b/apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/vendors/[riskId]/page.tsx index 763bc54db4..02f99d4561 100644 --- a/apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/vendors/[riskId]/page.tsx +++ b/apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/vendors/[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"; @@ -17,235 +17,225 @@ import type { RiskTaskStatus } from "@bubba/db/types"; import { Card, CardContent, CardHeader, CardTitle } from "@bubba/ui/card"; import type { Metadata } from "next"; import { setStaticParamsLocale } from "next-international/server"; -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("/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"], -); - -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"], -); - -const getUsers = unstable_cache( - async (organizationId: string) => { - const users = await db.user.findMany({ - where: { organizationId: organizationId }, - }); - - return users; - }, - ["users-cache"], -); +const getRisk = async (riskId: string, organizationId: string) => { + const risk = await db.risk.findUnique({ + where: { + id: riskId, + organizationId: organizationId, + }, + include: { + owner: true, + }, + }); + + return risk; +}; + +const getTasks = 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 }; +}; + +const getUsers = async (organizationId: string) => { + const users = await db.user.findMany({ + where: { organizationId: organizationId }, + }); + + return users; +}; 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]/vendors/[riskId]/tasks/[taskId]/page.tsx b/apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/vendors/[riskId]/tasks/[taskId]/page.tsx index 532a3335a7..e1a97d1936 100644 --- a/apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/vendors/[riskId]/tasks/[taskId]/page.tsx +++ b/apps/app/src/app/[locale]/(app)/(dashboard)/[orgId]/vendors/[riskId]/tasks/[taskId]/page.tsx @@ -1,87 +1,80 @@ import { auth } from "@/auth"; +import { TaskAttachments } from "@/components/risks/tasks/task-attachments"; import { TaskComment } from "@/components/risks/tasks/task-comments"; import { TaskOverview } from "@/components/risks/tasks/task-overview"; -import { TaskAttachments } from "@/components/risks/tasks/task-attachments"; import { getI18n } from "@/locales/server"; import { db } from "@bubba/db"; import type { Metadata } from "next"; import { setStaticParamsLocale } from "next-international/server"; -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("/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, - }, - }); +const getTask = 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; +}; -const getUsers = unstable_cache( - async (organizationId: string) => { - const users = await db.user.findMany({ - where: { organizationId: organizationId }, - }); +const getUsers = async (organizationId: string) => { + const users = await db.user.findMany({ + where: { organizationId: organizationId }, + }); - return users; - }, - ["users-cache"], -); + return users; +}; 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)/setup/page.tsx b/apps/app/src/app/[locale]/(app)/setup/page.tsx index 66a03c985d..3264b8cb7b 100644 --- a/apps/app/src/app/[locale]/(app)/setup/page.tsx +++ b/apps/app/src/app/[locale]/(app)/setup/page.tsx @@ -1,7 +1,6 @@ import { Onboarding } from "@/components/forms/create-organization-form"; import { db } from "@bubba/db"; import type { Metadata } from "next"; -import { cache } from "react"; export const metadata: Metadata = { title: "Organization Setup | Comp AI", @@ -13,10 +12,10 @@ export default async function Page() { return ; } -const getFrameworks = cache(async () => { +const getFrameworks = async () => { return await db.framework.findMany({ orderBy: { name: "asc", }, }); -}); +}; diff --git a/apps/app/src/components/org-menu.tsx b/apps/app/src/components/org-menu.tsx index 591c7ff36b..6d7c0076b2 100644 --- a/apps/app/src/components/org-menu.tsx +++ b/apps/app/src/components/org-menu.tsx @@ -1,38 +1,38 @@ import { auth } from "@/auth"; -import { Skeleton } from "@bubba/ui/skeleton"; -import { cache, Suspense } from "react"; -import { OrganizationSwitcher } from "./organization-switcher"; -import { redirect } from "next/navigation"; import { getOrganizations } from "@/data/getOrganizations"; import { db } from "@bubba/db"; +import { Skeleton } from "@bubba/ui/skeleton"; +import { redirect } from "next/navigation"; +import { Suspense } from "react"; +import { OrganizationSwitcher } from "./organization-switcher"; export async function OrgMenu() { - const session = await auth(); - const { organizations } = await getOrganizations(); - const frameworks = await getFrameworks(); + const session = await auth(); + const { organizations } = await getOrganizations(); + const frameworks = await getFrameworks(); - const user = session?.user; - const currentOrganizationId = user?.organizationId; + const user = session?.user; + const currentOrganizationId = user?.organizationId; - if (!currentOrganizationId) { - redirect("/"); - } + if (!currentOrganizationId) { + redirect("/"); + } - return ( - }> - - - ); + return ( + }> + + + ); } -const getFrameworks = cache(async () => { - return await db.framework.findMany({ - orderBy: { - name: "asc", - }, - }); -}); +const getFrameworks = async () => { + return await db.framework.findMany({ + orderBy: { + name: "asc", + }, + }); +}; diff --git a/apps/app/src/components/policies/charts/policies-by-assignee.tsx b/apps/app/src/components/policies/charts/policies-by-assignee.tsx index 2c04f4671c..66ef9fa841 100644 --- a/apps/app/src/components/policies/charts/policies-by-assignee.tsx +++ b/apps/app/src/components/policies/charts/policies-by-assignee.tsx @@ -1,235 +1,230 @@ -import React, { type CSSProperties } from "react"; import { getI18n } from "@/locales/server"; import { db } from "@bubba/db"; import { Card, CardContent, CardHeader, CardTitle } from "@bubba/ui/card"; -import { unstable_cache } from "next/cache"; +import type { CSSProperties } from "react"; interface Props { - organizationId: string; + organizationId: string; } interface UserPolicyStats { - user: { - id: string; - name: string | null; - image: string | null; - }; - totalPolicies: number | undefined; - publishedPolicies: number | undefined; - draftPolicies: number | undefined; - archivedPolicies: number | undefined; - needsReviewPolicies: number | undefined; + user: { + id: string; + name: string | null; + image: string | null; + }; + totalPolicies: number | undefined; + publishedPolicies: number | undefined; + draftPolicies: number | undefined; + archivedPolicies: number | undefined; + needsReviewPolicies: number | undefined; } const policyStatus = { - published: "bg-primary", - draft: "bg-[var(--chart-open)]", - archived: "bg-[var(--chart-pending)]", - needs_review: "bg-[hsl(var(--destructive))]", + published: "bg-primary", + draft: "bg-[var(--chart-open)]", + archived: "bg-[var(--chart-pending)]", + needs_review: "bg-[hsl(var(--destructive))]", }; export async function PoliciesByAssignee({ organizationId }: Props) { - const t = await getI18n(); - const userStats = await userData(organizationId); + const t = await getI18n(); + const userStats = await userData(organizationId); - const stats: UserPolicyStats[] = userStats.map((user) => ({ - user: { - id: user.id, - name: user.name, - image: user.image, - }, - totalPolicies: user.organization?.OrganizationPolicy.length, - publishedPolicies: user.organization?.OrganizationPolicy.filter( - (policy) => policy.status === "published", - ).length, - draftPolicies: user.organization?.OrganizationPolicy.filter( - (policy) => policy.status === "draft", - ).length, - archivedPolicies: user.organization?.OrganizationPolicy.filter( - (policy) => policy.status === "archived", - ).length, - needsReviewPolicies: user.organization?.OrganizationPolicy.filter( - (policy) => policy.status === "needs_review", - ).length, - })); + const stats: UserPolicyStats[] = userStats.map((user) => ({ + user: { + id: user.id, + name: user.name, + image: user.image, + }, + totalPolicies: user.organization?.OrganizationPolicy.length, + publishedPolicies: user.organization?.OrganizationPolicy.filter( + (policy) => policy.status === "published", + ).length, + draftPolicies: user.organization?.OrganizationPolicy.filter( + (policy) => policy.status === "draft", + ).length, + archivedPolicies: user.organization?.OrganizationPolicy.filter( + (policy) => policy.status === "archived", + ).length, + needsReviewPolicies: user.organization?.OrganizationPolicy.filter( + (policy) => policy.status === "needs_review", + ).length, + })); - stats.sort((a, b) => (b.totalPolicies ?? 0) - (a.totalPolicies ?? 0)); + stats.sort((a, b) => (b.totalPolicies ?? 0) - (a.totalPolicies ?? 0)); - return ( - - - {t("policies.dashboard.policies_by_assignee")} - - -
- {stats.map((stat) => ( -
-
-

{stat.user.name || "Unknown User"}

- - {stat.totalPolicies} {t("policies.policies")} - -
+ return ( + + + {t("policies.dashboard.policies_by_assignee")} + + +
+ {stats.map((stat) => ( +
+
+

{stat.user.name || "Unknown User"}

+ + {stat.totalPolicies} {t("policies.policies")} + +
- + -
-
-
- - {t("common.status.published")} ({stat.publishedPolicies}) - -
-
-
- - {t("common.status.draft")} ({stat.draftPolicies}) - -
-
-
- - {t("common.status.archived")} ({stat.archivedPolicies}) - -
-
-
- - {t("common.status.needs_review")} ( - {stat.needsReviewPolicies}) - -
-
-
- ))} -
- - - ); +
+
+
+ + {t("common.status.published")} ({stat.publishedPolicies}) + +
+
+
+ + {t("common.status.draft")} ({stat.draftPolicies}) + +
+
+
+ + {t("common.status.archived")} ({stat.archivedPolicies}) + +
+
+
+ + {t("common.status.needs_review")} ( + {stat.needsReviewPolicies}) + +
+
+
+ ))} +
+ + + ); } function RiskBarChart({ stat, t }: { stat: UserPolicyStats; t: any }) { - const data = [ - ...(stat.publishedPolicies && stat.publishedPolicies > 0 - ? [ - { - key: "published", - value: stat.publishedPolicies, - color: policyStatus.published, - label: t("common.status.published"), - }, - ] - : []), - ...(stat.draftPolicies && stat.draftPolicies > 0 - ? [ - { - key: "draft", - value: stat.draftPolicies, - color: policyStatus.draft, - label: t("common.status.draft"), - }, - ] - : []), - ...(stat.archivedPolicies && stat.archivedPolicies > 0 - ? [ - { - key: "archived", - value: stat.archivedPolicies, - color: policyStatus.archived, - label: t("common.status.archived"), - }, - ] - : []), - ]; + const data = [ + ...(stat.publishedPolicies && stat.publishedPolicies > 0 + ? [ + { + key: "published", + value: stat.publishedPolicies, + color: policyStatus.published, + label: t("common.status.published"), + }, + ] + : []), + ...(stat.draftPolicies && stat.draftPolicies > 0 + ? [ + { + key: "draft", + value: stat.draftPolicies, + color: policyStatus.draft, + label: t("common.status.draft"), + }, + ] + : []), + ...(stat.archivedPolicies && stat.archivedPolicies > 0 + ? [ + { + key: "archived", + value: stat.archivedPolicies, + color: policyStatus.archived, + label: t("common.status.archived"), + }, + ] + : []), + ]; - const gap = 0.3; - const totalValue = stat.totalPolicies ?? 0; - const barHeight = 12; - const totalWidth = totalValue + gap * (data.length - 1); - let cumulativeWidth = 0; - const cornerRadius = 0; + const gap = 0.3; + const totalValue = stat.totalPolicies ?? 0; + const barHeight = 12; + const totalWidth = totalValue + gap * (data.length - 1); + let cumulativeWidth = 0; + const cornerRadius = 0; - if (totalValue === 0) { - return
; - } + if (totalValue === 0) { + return
; + } - return ( -
-
+
- {data.map((d, index) => { - const barWidth = (d.value / totalWidth) * 100; - const xPosition = cumulativeWidth; - cumulativeWidth += barWidth + gap; + > + {data.map((d, index) => { + const barWidth = (d.value / totalWidth) * 100; + const xPosition = cumulativeWidth; + cumulativeWidth += barWidth + gap; - return ( -
-
-
- ); - })} -
-
- ); + return ( +
+
+
+ ); + })} +
+
+ ); } -const userData = unstable_cache( - async (organizationId: string) => { - return await db.user.findMany({ - where: { - organizationId, - }, - select: { - id: true, - name: true, - image: true, - organization: { - select: { - OrganizationPolicy: { - select: { - status: true, - }, - }, - }, - }, - }, - }); - }, - ["users-policies-data-cache"], - { revalidate: 3600 }, // Cache for 1 hour -); +const userData = async (organizationId: string) => { + return await db.user.findMany({ + where: { + organizationId, + }, + select: { + id: true, + name: true, + image: true, + organization: { + select: { + OrganizationPolicy: { + select: { + status: true, + }, + }, + }, + }, + }, + }); +}; diff --git a/apps/app/src/components/risks/charts/risks-assignee.tsx b/apps/app/src/components/risks/charts/risks-assignee.tsx index d46b400d80..41fa216726 100644 --- a/apps/app/src/components/risks/charts/risks-assignee.tsx +++ b/apps/app/src/components/risks/charts/risks-assignee.tsx @@ -2,236 +2,231 @@ import React, { type CSSProperties } from "react"; import { getI18n } from "@/locales/server"; import { db } from "@bubba/db"; import { Card, CardContent, CardHeader, CardTitle } from "@bubba/ui/card"; -import { unstable_cache } from "next/cache"; interface Props { - organizationId: string; + organizationId: string; } interface UserRiskStats { - user: { - id: string; - name: string | null; - image: string | null; - }; - totalRisks: number; - openRisks: number; - pendingRisks: number; - closedRisks: number; - archivedRisks: number; + user: { + id: string; + name: string | null; + image: string | null; + }; + totalRisks: number; + openRisks: number; + pendingRisks: number; + closedRisks: number; + archivedRisks: number; } const riskStatusColors = { - open: "bg-[var(--chart-open)]", - pending: "bg-[var(--chart-pending)]", - closed: "bg-[var(--chart-closed)]", - archived: "bg-[var(--chart-archived)]", + open: "bg-[var(--chart-open)]", + pending: "bg-[var(--chart-pending)]", + closed: "bg-[var(--chart-closed)]", + archived: "bg-[var(--chart-archived)]", }; export async function RisksAssignee({ organizationId }: Props) { - const t = await getI18n(); - const userStats = await userData(organizationId); - - const stats: UserRiskStats[] = userStats.map((user) => ({ - user: { - id: user.id, - name: user.name, - image: user.image, - }, - totalRisks: user.Risk.length, - openRisks: user.Risk.filter((risk) => risk.status === "open").length, - pendingRisks: user.Risk.filter((risk) => risk.status === "pending").length, - closedRisks: user.Risk.filter((risk) => risk.status === "closed").length, - archivedRisks: user.Risk.filter((risk) => risk.status === "archived") - .length, - })); - - stats.sort((a, b) => b.totalRisks - a.totalRisks); - - return ( - - - {t("risk.dashboard.risks_by_assignee")} - - -
- {stats.map((stat) => ( -
-
-

{stat.user.name || "Unknown User"}

- - {stat.totalRisks} {t("risk.risks")} - -
- - - -
-
-
- - {t("common.status.open")} ({stat.openRisks}) - -
-
-
- - {t("common.status.pending")} ({stat.pendingRisks}) - -
-
-
- - {t("common.status.closed")} ({stat.closedRisks}) - -
- -
-
- - {t("common.status.archived")} ({stat.archivedRisks}) - -
-
-
- ))} -
- - - ); + const t = await getI18n(); + const userStats = await userData(organizationId); + + const stats: UserRiskStats[] = userStats.map((user) => ({ + user: { + id: user.id, + name: user.name, + image: user.image, + }, + totalRisks: user.Risk.length, + openRisks: user.Risk.filter((risk) => risk.status === "open").length, + pendingRisks: user.Risk.filter((risk) => risk.status === "pending").length, + closedRisks: user.Risk.filter((risk) => risk.status === "closed").length, + archivedRisks: user.Risk.filter((risk) => risk.status === "archived") + .length, + })); + + stats.sort((a, b) => b.totalRisks - a.totalRisks); + + return ( + + + {t("risk.dashboard.risks_by_assignee")} + + +
+ {stats.map((stat) => ( +
+
+

{stat.user.name || "Unknown User"}

+ + {stat.totalRisks} {t("risk.risks")} + +
+ + + +
+
+
+ + {t("common.status.open")} ({stat.openRisks}) + +
+
+
+ + {t("common.status.pending")} ({stat.pendingRisks}) + +
+
+
+ + {t("common.status.closed")} ({stat.closedRisks}) + +
+ +
+
+ + {t("common.status.archived")} ({stat.archivedRisks}) + +
+
+
+ ))} +
+ + + ); } function RiskBarChart({ stat, t }: { stat: UserRiskStats; t: any }) { - const data = [ - ...(stat.openRisks > 0 - ? [ - { - key: "open", - value: stat.openRisks, - color: riskStatusColors.open, - label: t("common.status.open"), - }, - ] - : []), - ...(stat.pendingRisks > 0 - ? [ - { - key: "pending", - value: stat.pendingRisks, - color: riskStatusColors.pending, - label: t("common.status.pending"), - }, - ] - : []), - ...(stat.closedRisks > 0 - ? [ - { - key: "closed", - value: stat.closedRisks, - color: riskStatusColors.closed, - label: t("common.status.closed"), - }, - ] - : []), - ...(stat.archivedRisks > 0 - ? [ - { - key: "archived", - value: stat.archivedRisks, - color: riskStatusColors.archived, - label: t("common.status.archived"), - }, - ] - : []), - ]; - - const gap = 0.3; - const totalValue = stat.totalRisks; - const barHeight = 12; - const totalWidth = totalValue + gap * (data.length - 1); - let cumulativeWidth = 0; - const cornerRadius = 0; - - if (totalValue === 0) { - return
; - } - - return ( -
-
0 + ? [ + { + key: "pending", + value: stat.pendingRisks, + color: riskStatusColors.pending, + label: t("common.status.pending"), + }, + ] + : []), + ...(stat.closedRisks > 0 + ? [ + { + key: "closed", + value: stat.closedRisks, + color: riskStatusColors.closed, + label: t("common.status.closed"), + }, + ] + : []), + ...(stat.archivedRisks > 0 + ? [ + { + key: "archived", + value: stat.archivedRisks, + color: riskStatusColors.archived, + label: t("common.status.archived"), + }, + ] + : []), + ]; + + const gap = 0.3; + const totalValue = stat.totalRisks; + const barHeight = 12; + const totalWidth = totalValue + gap * (data.length - 1); + let cumulativeWidth = 0; + const cornerRadius = 0; + + if (totalValue === 0) { + return
; + } + + return ( +
+
- {data.map((d, index) => { - const barWidth = (d.value / totalWidth) * 100; - const xPosition = cumulativeWidth; - cumulativeWidth += barWidth + gap; - - return ( -
-
-
- ); - })} -
-
- ); + > + {data.map((d, index) => { + const barWidth = (d.value / totalWidth) * 100; + const xPosition = cumulativeWidth; + cumulativeWidth += barWidth + gap; + + return ( +
+
+
+ ); + })} +
+
+ ); } -const userData = unstable_cache( - async (organizationId: string) => { - return await db.user.findMany({ - where: { - organizationId, - Risk: { - some: {}, - }, - }, - select: { - id: true, - name: true, - image: true, - Risk: { - select: { - status: true, - }, - }, - }, - }); - }, - ["users-with-risks"], - { tags: ["risks", "users"] }, -); +const userData = async (organizationId: string) => { + return await db.user.findMany({ + where: { + organizationId, + Risk: { + some: {}, + }, + }, + select: { + id: true, + name: true, + image: true, + Risk: { + select: { + status: true, + }, + }, + }, + }); +}; diff --git a/apps/app/src/components/risks/charts/risks-by-assignee.tsx b/apps/app/src/components/risks/charts/risks-by-assignee.tsx index b35d4cc0c6..a6a737c541 100644 --- a/apps/app/src/components/risks/charts/risks-by-assignee.tsx +++ b/apps/app/src/components/risks/charts/risks-by-assignee.tsx @@ -1,13 +1,10 @@ -import { auth } from "@/auth"; import { getInitials } from "@/lib/utils"; import { getI18n } from "@/locales/server"; import { db } from "@bubba/db"; import { Avatar, AvatarFallback, AvatarImage } from "@bubba/ui/avatar"; import { Card, CardContent, CardHeader, CardTitle } from "@bubba/ui/card"; import { ScrollArea } from "@bubba/ui/scroll-area"; -import { unstable_cache } from "next/cache"; import Link from "next/link"; -import { redirect } from "next/navigation"; interface Props { organizationId: string; @@ -168,27 +165,23 @@ export async function RisksByAssignee({ organizationId }: Props) { ); } -const userData = unstable_cache( - async (organizationId: string) => { - return await db.user.findMany({ - where: { - organizationId, - Risk: { - some: {}, - }, +const userData = async (organizationId: string) => { + return await db.user.findMany({ + where: { + organizationId, + Risk: { + some: {}, }, - select: { - id: true, - name: true, - image: true, - Risk: { - select: { - status: true, - }, + }, + select: { + id: true, + name: true, + image: true, + Risk: { + select: { + status: true, }, }, - }); - }, - ["users-with-risks"], - { tags: ["risks", "users"] }, -); + }, + }); +}; diff --git a/apps/app/src/components/risks/charts/risks-by-department.tsx b/apps/app/src/components/risks/charts/risks-by-department.tsx index bc111cfb47..c2c0c2f22f 100644 --- a/apps/app/src/components/risks/charts/risks-by-department.tsx +++ b/apps/app/src/components/risks/charts/risks-by-department.tsx @@ -1,8 +1,7 @@ +import { getI18n } from "@/locales/server"; import { db } from "@bubba/db"; import { Card, CardContent, CardHeader, CardTitle } from "@bubba/ui/card"; import { DepartmentChart } from "./department-chart"; -import { unstable_cache } from "next/cache"; -import { getI18n } from "@/locales/server"; const ALL_DEPARTMENTS = ["none", "admin", "gov", "hr", "it", "itsm", "qms"]; @@ -54,16 +53,12 @@ export async function RisksByDepartment({ organizationId }: Props) { ); } -const getRisksByDepartment = unstable_cache( - async (organizationId: string) => { - const risksByDepartment = await db.risk.groupBy({ - by: ["department"], - where: { organizationId }, - _count: true, - }); +const getRisksByDepartment = async (organizationId: string) => { + const risksByDepartment = await db.risk.groupBy({ + by: ["department"], + where: { organizationId }, + _count: true, + }); - return risksByDepartment; - }, - ["risks-by-department"], - { tags: ["risks", "departments"] }, -); + return risksByDepartment; +}; diff --git a/apps/app/src/components/risks/charts/risks-by-status.tsx b/apps/app/src/components/risks/charts/risks-by-status.tsx index ca2ec6be3d..8e2fb5b4da 100644 --- a/apps/app/src/components/risks/charts/risks-by-status.tsx +++ b/apps/app/src/components/risks/charts/risks-by-status.tsx @@ -1,43 +1,38 @@ +import { getI18n } from "@/locales/server"; import { db } from "@bubba/db"; +import { Card, CardContent, CardHeader, CardTitle } from "@bubba/ui/card"; import { StatusChart } from "./status-chart"; -import { unstable_cache } from "next/cache"; -import { Card, CardHeader, CardTitle, CardContent } from "@bubba/ui/card"; -import { getI18n } from "@/locales/server"; interface Props { - organizationId: string; + organizationId: string; } export async function RisksByStatus({ organizationId }: Props) { - const t = await getI18n(); + const t = await getI18n(); - const risks = await getRisksByStatus(organizationId); + const risks = await getRisksByStatus(organizationId); - const data = risks.map((risk) => ({ - name: risk.status, - value: risk._count, - })); + const data = risks.map((risk) => ({ + name: risk.status, + value: risk._count, + })); - return ( - - - {t("dashboard.risk_status")} - - - - - - ); + return ( + + + {t("dashboard.risk_status")} + + + + + + ); } -const getRisksByStatus = unstable_cache( - async (organizationId: string) => { - return await db.risk.groupBy({ - by: ["status"], - where: { organizationId }, - _count: true, - }); - }, - ["risks-by-status"], - { tags: ["risks", "status"] }, -); +const getRisksByStatus = async (organizationId: string) => { + return await db.risk.groupBy({ + by: ["status"], + where: { organizationId }, + _count: true, + }); +}; diff --git a/apps/app/src/components/tests/charts/tests-by-assignee.tsx b/apps/app/src/components/tests/charts/tests-by-assignee.tsx index 10ae0cda6a..961377ceae 100644 --- a/apps/app/src/components/tests/charts/tests-by-assignee.tsx +++ b/apps/app/src/components/tests/charts/tests-by-assignee.tsx @@ -1,242 +1,235 @@ -import React, { type CSSProperties } from "react"; import { getI18n } from "@/locales/server"; import { db } from "@bubba/db"; import { Card, CardContent, CardHeader, CardTitle } from "@bubba/ui/card"; -import { unstable_cache } from "next/cache"; +import type { CSSProperties } from "react"; interface Props { - organizationId: string; + organizationId: string; } interface UserTestStats { - user: { - id: string; - name: string | null; - image: string | null; - }; - totalTests: number; - passedTests: number; - failedTests: number; - unsupportedTests: number; + user: { + id: string; + name: string | null; + image: string | null; + }; + totalTests: number; + passedTests: number; + failedTests: number; + unsupportedTests: number; } interface TestData { - status: string; - assignedUserId: string | null; + status: string; + assignedUserId: string | null; } interface UserData { - id: string; - name: string | null; - image: string | null; - OrganizationIntegrationResults: TestData[]; + id: string; + name: string | null; + image: string | null; + OrganizationIntegrationResults: TestData[]; } const testStatus = { - passed: "bg-[var(--chart-closed)]", - failed: "bg-[hsl(var(--destructive))]", - unsupported: "bg-[hsl(var(--muted-foreground))]", + passed: "bg-[var(--chart-closed)]", + failed: "bg-[hsl(var(--destructive))]", + unsupported: "bg-[hsl(var(--muted-foreground))]", }; export async function TestsByAssignee({ organizationId }: Props) { - const t = await getI18n(); - const userStats = await userData(organizationId); - console.log(userStats); - const stats: UserTestStats[] = userStats.map((user) => ({ - user: { - id: user.id, - name: user.name, - image: user.image, - }, - totalTests: user.OrganizationIntegrationResults.length, - passedTests: user.OrganizationIntegrationResults.filter( - (test) => test.status.toUpperCase() === "passed".toUpperCase(), - ).length, - failedTests: user.OrganizationIntegrationResults.filter( - (test) => test.status.toUpperCase() === "failed".toUpperCase(), - ).length, - unsupportedTests: user.OrganizationIntegrationResults.filter( - (test) => test.status.toUpperCase() === "unsupported".toUpperCase(), - ).length, - })); - - stats.sort((a, b) => b.totalTests - a.totalTests); - - return ( - - - {t("tests.dashboard.tests_by_assignee")} - - -
- {stats.map((stat) => ( -
-
-

{stat.user.name || "Unknown User"}

- - {stat.totalTests} Tests - -
- - - -
-
-
- - {t("tests.dashboard.passed")} ({stat.passedTests}) - -
-
-
- - {t("tests.dashboard.failed")} ({stat.failedTests}) - -
-
-
- - Unsupported ({stat.unsupportedTests}) - -
-
-
- ))} -
- - - ); + const t = await getI18n(); + const userStats = await userData(organizationId); + console.log(userStats); + const stats: UserTestStats[] = userStats.map((user) => ({ + user: { + id: user.id, + name: user.name, + image: user.image, + }, + totalTests: user.OrganizationIntegrationResults.length, + passedTests: user.OrganizationIntegrationResults.filter( + (test) => test.status.toUpperCase() === "passed".toUpperCase(), + ).length, + failedTests: user.OrganizationIntegrationResults.filter( + (test) => test.status.toUpperCase() === "failed".toUpperCase(), + ).length, + unsupportedTests: user.OrganizationIntegrationResults.filter( + (test) => test.status.toUpperCase() === "unsupported".toUpperCase(), + ).length, + })); + + stats.sort((a, b) => b.totalTests - a.totalTests); + + return ( + + + {t("tests.dashboard.tests_by_assignee")} + + +
+ {stats.map((stat) => ( +
+
+

{stat.user.name || "Unknown User"}

+ + {stat.totalTests} Tests + +
+ + + +
+
+
+ + {t("tests.dashboard.passed")} ({stat.passedTests}) + +
+
+
+ + {t("tests.dashboard.failed")} ({stat.failedTests}) + +
+
+
+ Unsupported ({stat.unsupportedTests}) +
+
+
+ ))} +
+ + + ); } function TestBarChart({ stat }: { stat: UserTestStats }) { - const data = [ - ...(stat.passedTests > 0 - ? [ - { - key: "passed", - value: stat.passedTests, - color: testStatus.passed, - label: 'passed', - }, - ] - : []), - ...(stat.failedTests > 0 - ? [ - { - key: "failed", - value: stat.failedTests, - color: testStatus.failed, - label: "failed", - }, - ] - : []), - ...(stat.unsupportedTests > 0 - ? [ - { - key: "unsupported", - value: stat.unsupportedTests, - color: testStatus.unsupported, - label: "unsupported", - }, - ] - : []), - ]; - - const gap = 0.3; - const totalValue = stat.totalTests; - const barHeight = 12; - const totalWidth = totalValue + gap * (data.length - 1); - let cumulativeWidth = 0; - const cornerRadius = 0; - - if (totalValue === 0) { - return
; - } - - return ( -
-
0 + ? [ + { + key: "failed", + value: stat.failedTests, + color: testStatus.failed, + label: "failed", + }, + ] + : []), + ...(stat.unsupportedTests > 0 + ? [ + { + key: "unsupported", + value: stat.unsupportedTests, + color: testStatus.unsupported, + label: "unsupported", + }, + ] + : []), + ]; + + const gap = 0.3; + const totalValue = stat.totalTests; + const barHeight = 12; + const totalWidth = totalValue + gap * (data.length - 1); + let cumulativeWidth = 0; + const cornerRadius = 0; + + if (totalValue === 0) { + return
; + } + + return ( +
+
- {data.map((d) => { - const barWidth = (d.value / totalWidth) * 100; - const xPosition = cumulativeWidth; - cumulativeWidth += barWidth + gap; - - return ( -
-
-
- ); - })} -
-
- ); + > + {data.map((d) => { + const barWidth = (d.value / totalWidth) * 100; + const xPosition = cumulativeWidth; + cumulativeWidth += barWidth + gap; + + return ( +
+
+
+ ); + })} +
+
+ ); } -const userData = unstable_cache( - async (organizationId: string): Promise => { - // Fetch users in the organization with their assigned test results - const users = await db.user.findMany({ - where: { - organizationId, - }, - select: { - id: true, - name: true, - image: true, - OrganizationIntegrationResults: { - select: { - status: true, - }, - where: { - organizationId, - NOT: { - assignedUserId: null - } - }, - }, - }, - }); - - console.log(users); - - return users as UserData[]; - }, - ["users-tests-data-cache-v3"], - { revalidate: 3600 }, -); +const userData = async (organizationId: string): Promise => { + // Fetch users in the organization with their assigned test results + const users = await db.user.findMany({ + where: { + organizationId, + }, + select: { + id: true, + name: true, + image: true, + OrganizationIntegrationResults: { + select: { + status: true, + }, + where: { + organizationId, + NOT: { + assignedUserId: null, + }, + }, + }, + }, + }); + + console.log(users); + + return users as UserData[]; +}; diff --git a/apps/app/src/components/vendors/charts/vendors-by-category.tsx b/apps/app/src/components/vendors/charts/vendors-by-category.tsx index bf460e4ce1..d1333a1cc4 100644 --- a/apps/app/src/components/vendors/charts/vendors-by-category.tsx +++ b/apps/app/src/components/vendors/charts/vendors-by-category.tsx @@ -1,9 +1,8 @@ +import { getI18n } from "@/locales/server"; import { db } from "@bubba/db"; import { VendorCategory } from "@bubba/db/types"; import { Card, CardContent, CardHeader, CardTitle } from "@bubba/ui/card"; import { VendorCategoryChart } from "./category-chart"; -import { unstable_cache } from "next/cache"; -import { getI18n } from "@/locales/server"; const VENDOR_CATEGORIES = Object.values(VendorCategory); @@ -58,16 +57,12 @@ export async function VendorsByCategory({ organizationId }: Props) { ); } -const getVendorsByCategory = unstable_cache( - async (organizationId: string) => { - const vendorsByCategory = await db.vendor.groupBy({ - by: ["category"], - where: { organizationId }, - _count: true, - }); +const getVendorsByCategory = async (organizationId: string) => { + const vendorsByCategory = await db.vendor.groupBy({ + by: ["category"], + where: { organizationId }, + _count: true, + }); - return vendorsByCategory; - }, - ["vendors-by-category"], - { tags: ["vendors", "categories"] }, -); + return vendorsByCategory; +}; diff --git a/apps/app/src/components/vendors/charts/vendors-by-status.tsx b/apps/app/src/components/vendors/charts/vendors-by-status.tsx index 28fe2c2d64..4d540854eb 100644 --- a/apps/app/src/components/vendors/charts/vendors-by-status.tsx +++ b/apps/app/src/components/vendors/charts/vendors-by-status.tsx @@ -1,8 +1,7 @@ +import { getI18n } from "@/locales/server"; import { db } from "@bubba/db"; +import { Card, CardContent, CardHeader, CardTitle } from "@bubba/ui/card"; import { StatusChart } from "./status-chart"; -import { unstable_cache } from "next/cache"; -import { Card, CardHeader, CardTitle, CardContent } from "@bubba/ui/card"; -import { getI18n } from "@/locales/server"; interface Props { organizationId: string; } @@ -29,14 +28,10 @@ export async function VendorsByStatus({ organizationId }: Props) { ); } -const getVendorsByStatus = unstable_cache( - async (organizationId: string) => { - return await db.vendor.groupBy({ - by: ["status"], - where: { organizationId }, - _count: true, - }); - }, - ["vendors-by-status"], - { tags: ["vendors", "status"] }, -); +const getVendorsByStatus = async (organizationId: string) => { + return await db.vendor.groupBy({ + by: ["status"], + where: { organizationId }, + _count: true, + }); +};