Add frameworks page

Signed-off-by: gearnode <bryan@frimin.fr>
This commit is contained in:
gearnode
2025-02-09 21:24:18 -08:00
parent 6c95643b26
commit 91e2326826
4 changed files with 212 additions and 0 deletions

View File

@@ -24,6 +24,7 @@ const HomePage = lazy(() => import("./pages/HomePage"));
const NotFoundPage = lazy(() => import("./pages/NotFoundPage"));
const PeoplesPage = lazy(() => import("./pages/PeoplesPage"));
const VendorsPage = lazy(() => import("./pages/VendorsPage"));
const FrameworksPage = lazy(() => import("./pages/FrameworksPage"));
function App() {
return (
@@ -39,6 +40,7 @@ function App() {
<Route index element={<HomePage />} />
<Route path="/peoples" element={<PeoplesPage />} />
<Route path="/vendors" element={<VendorsPage />} />
<Route path="/frameworks" element={<FrameworksPage />} />
</Route>
<Route path="*" element={<NotFoundPage />} />
</Routes>

View File

@@ -36,6 +36,11 @@ const staticData = {
avatar: "/avatars/shadcn.jpg",
},
navMain: [
{
title: "Frameworks",
url: "/frameworks",
icon: BookOpen,
},
{
title: "Peoples",
url: "/peoples",

View File

@@ -0,0 +1,76 @@
import * as React from "react"
import { cn } from "@/lib/utils"
const Card = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => (
<div
ref={ref}
className={cn(
"rounded-xl border bg-card text-card-foreground shadow",
className
)}
{...props}
/>
))
Card.displayName = "Card"
const CardHeader = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => (
<div
ref={ref}
className={cn("flex flex-col space-y-1.5 p-6", className)}
{...props}
/>
))
CardHeader.displayName = "CardHeader"
const CardTitle = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => (
<div
ref={ref}
className={cn("font-semibold leading-none tracking-tight", className)}
{...props}
/>
))
CardTitle.displayName = "CardTitle"
const CardDescription = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => (
<div
ref={ref}
className={cn("text-sm text-muted-foreground", className)}
{...props}
/>
))
CardDescription.displayName = "CardDescription"
const CardContent = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => (
<div ref={ref} className={cn("p-6 pt-0", className)} {...props} />
))
CardContent.displayName = "CardContent"
const CardFooter = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => (
<div
ref={ref}
className={cn("flex items-center p-6 pt-0", className)}
{...props}
/>
))
CardFooter.displayName = "CardFooter"
export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent }

View File

@@ -0,0 +1,129 @@
import { Suspense, useEffect } from "react";
import {
graphql,
PreloadedQuery,
usePreloadedQuery,
useQueryLoader,
} from "react-relay";
import { Globe2, CheckCircle } from "lucide-react";
import { Card, CardContent } from "@/components/ui/card";
import { Badge } from "@/components/ui/badge";
import type { FrameworksPageQuery as FrameworksPageQueryType } from "./__generated__/FrameworksPageQuery.graphql";
const FrameworksPageQuery = graphql`
query FrameworksPageQuery {
node(id: "AZSfP_xAcAC5IAAAAAAltA") {
id
... on Organization {
frameworks {
edges {
node {
id
name
description
controls {
edges {
node {
id
state
}
}
}
createdAt
updatedAt
}
}
}
}
}
}
`;
function FrameworksPageContent({
queryRef,
}: {
queryRef: PreloadedQuery<FrameworksPageQueryType>;
}) {
const data = usePreloadedQuery(FrameworksPageQuery, queryRef);
const frameworks = data.node.frameworks?.edges.map(edge => edge?.node) ?? [];
return (
<div className="min-h-screen bg-background p-6">
<div className="mb-8">
<h1 className="text-2xl font-semibold mb-2">Framework</h1>
<p className="text-muted-foreground">
Compliance frameworks like SOC 2 and ISO 27001 provide guidelines to secure sensitive data, manage risks, and
build trust. SOC 2 focuses on customer data protection via five Trust Principles, while ISO 27001 establishes
a comprehensive Information Security Management System for global standards.
</p>
</div>
<div className="grid gap-6 md:grid-cols-2">
{frameworks.map((framework) => (
<Card key={framework?.id} className="bg-card/50">
<CardContent className="p-6">
<div className="relative mb-6">
<Badge className="absolute right-0 top-0 bg-green-500/10 text-green-500 hover:bg-green-500/20">
Active
</Badge>
<div className="bg-green-500/10 w-24 h-24 rounded-full flex items-center justify-center mb-4">
<Globe2 className="w-12 h-12 text-green-500" />
<div className="absolute text-xs font-medium text-green-500">{framework?.name}</div>
</div>
<h2 className="text-xl font-semibold mb-2">{framework?.name}</h2>
<p className="text-sm text-muted-foreground">
{framework?.description}
</p>
</div>
<div className="flex items-center text-muted-foreground text-sm">
<div className="flex items-center">
<CheckCircle className="w-4 h-4 mr-2" />
{framework?.controls?.edges.length} Controls
</div>
</div>
</CardContent>
</Card>
))}
</div>
</div>
);
}
function FrameworksPageFallback() {
return (
<div className="min-h-screen bg-background p-6">
<div className="mb-8">
<div className="h-8 w-48 bg-muted animate-pulse rounded" />
<div className="h-4 w-96 bg-muted animate-pulse rounded mt-2" />
</div>
<div className="grid gap-6 md:grid-cols-2">
{[1, 2].map((i) => (
<Card key={i} className="bg-card/50">
<CardContent className="p-6">
<div className="relative mb-6">
<div className="bg-muted w-24 h-24 rounded-full animate-pulse mb-4" />
<div className="h-6 w-48 bg-muted animate-pulse rounded mb-2" />
<div className="h-20 w-full bg-muted animate-pulse rounded" />
</div>
<div className="h-4 w-32 bg-muted animate-pulse rounded" />
</CardContent>
</Card>
))}
</div>
</div>
);
}
export default function FrameworksPage() {
const [queryRef, loadQuery] = useQueryLoader<FrameworksPageQueryType>(FrameworksPageQuery);
useEffect(() => {
loadQuery({});
}, [loadQuery]);
return (
<Suspense fallback={<FrameworksPageFallback />}>
{queryRef && <FrameworksPageContent queryRef={queryRef} />}
</Suspense>
);
}