From 79c69b681ec3512390720e552535859557f12cb1 Mon Sep 17 00:00:00 2001 From: gearnode Date: Wed, 5 Mar 2025 10:58:15 +0100 Subject: [PATCH] Improve policy overview style Signed-off-by: gearnode --- apps/console/package.json | 3 +- apps/console/src/components/ui/tabs.tsx | 52 +++ apps/console/src/pages/PolicyOverviewPage.tsx | 410 ++++++++++++------ package-lock.json | 39 +- 4 files changed, 370 insertions(+), 134 deletions(-) create mode 100644 apps/console/src/components/ui/tabs.tsx diff --git a/apps/console/package.json b/apps/console/package.json index c5cad0610..fb43e7c4b 100644 --- a/apps/console/package.json +++ b/apps/console/package.json @@ -23,6 +23,7 @@ "@radix-ui/react-select": "^2.0.0", "@radix-ui/react-separator": "^1.1.2", "@radix-ui/react-slot": "^1.1.2", + "@radix-ui/react-tabs": "^1.1.3", "@radix-ui/react-toast": "^1.2.6", "@radix-ui/react-tooltip": "^1.1.8", "class-variance-authority": "^0.7.1", @@ -38,7 +39,7 @@ "react-relay": "^18.2.0", "react-router": "^7.1.5", "relay-runtime": "^18.2.0", - "tailwind-merge": "^3.0.1", + "tailwind-merge": "^3.0.2", "tailwindcss-animate": "^1.0.7" }, "devDependencies": { diff --git a/apps/console/src/components/ui/tabs.tsx b/apps/console/src/components/ui/tabs.tsx new file mode 100644 index 000000000..cc2b747bd --- /dev/null +++ b/apps/console/src/components/ui/tabs.tsx @@ -0,0 +1,52 @@ +import * as React from "react"; +import * as TabsPrimitive from "@radix-ui/react-tabs"; +import { cn } from "@/lib/utils"; + +const Tabs = TabsPrimitive.Root; + +const TabsList = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +TabsList.displayName = TabsPrimitive.List.displayName; + +const TabsTrigger = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +TabsTrigger.displayName = TabsPrimitive.Trigger.displayName; + +const TabsContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +TabsContent.displayName = TabsPrimitive.Content.displayName; + +export { Tabs, TabsList, TabsTrigger, TabsContent }; diff --git a/apps/console/src/pages/PolicyOverviewPage.tsx b/apps/console/src/pages/PolicyOverviewPage.tsx index 67a0e48c2..2bd5903a0 100644 --- a/apps/console/src/pages/PolicyOverviewPage.tsx +++ b/apps/console/src/pages/PolicyOverviewPage.tsx @@ -1,4 +1,4 @@ -import { Suspense, useEffect } from "react"; +import { Suspense, useEffect, useState } from "react"; import { useParams, Link } from "react-router"; import { graphql, @@ -6,10 +6,11 @@ import { usePreloadedQuery, useQueryLoader, } from "react-relay"; -import { Edit } from "lucide-react"; -import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; +import { Edit, Download, Shield, User, FileText } from "lucide-react"; +import { Card, CardContent } from "@/components/ui/card"; import { Button } from "@/components/ui/button"; import { Badge } from "@/components/ui/badge"; +import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import type { PolicyOverviewPageQuery as PolicyOverviewPageQueryType } from "./__generated__/PolicyOverviewPageQuery.graphql"; import { Helmet } from "react-helmet-async"; import "../styles/policy-content.css"; @@ -37,110 +38,229 @@ function PolicyOverviewPageContent({ const data = usePreloadedQuery(PolicyOverviewPageQuery, queryRef); const policy = data.node; const { organizationId } = useParams(); + const [activeTab, setActiveTab] = useState("content"); - const getStatusBadge = (status: string | undefined) => { - if (!status) return null; + // Extract a short description from the content + const getDescription = (content: string | undefined) => { + if (!content) return "No description available"; - switch (status) { - case "ACTIVE": - return ( - - Active - - ); - case "DRAFT": - return ( - - Draft - - ); - default: - return ( - - {status} - - ); - } + // Remove HTML tags + const withoutTags = content.replace(/<[^>]*>/g, ""); + // Decode HTML entities + const decoded = withoutTags + .replace(/&/g, "&") + .replace(/</g, "<") + .replace(/>/g, ">") + .replace(/"/g, '"') + .replace(/'/g, "'") + .replace(/ /g, " "); + + // Get first paragraph or first 150 characters + const firstParagraph = decoded.split("\n\n")[0].trim(); + return firstParagraph.length > 150 + ? firstParagraph.substring(0, 150) + "..." + : firstParagraph; }; const formatDate = (dateString: string | undefined) => { if (!dateString) return "N/A"; - const date = new Date(dateString); - return new Intl.DateTimeFormat("en-US", { - year: "numeric", - month: "long", - day: "numeric", - hour: "2-digit", - minute: "2-digit", - }).format(date); + return date.toISOString().split("T")[0]; // YYYY-MM-DD format }; return ( -
-
-
-
-

{policy.name}

-
-
- -
-
-
- -
-
- - - Policy Details - - -
-
-

- Status -

-
{getStatusBadge(policy.status)}
+
+
+
+ + +
+
+
-
-

- Created -

-

{formatDate(policy.createdAt)}

+
+ + SOC2 + + + Security + + {policy.status && ( + + {policy.status === "ACTIVE" + ? "Active" + : policy.status === "DRAFT" + ? "Draft" + : policy.status} + + )}
-
-

- Last Updated -

-

{formatDate(policy.updatedAt)}

+
+
+ +

{policy.name}

+ +

+ {getDescription(policy.content)} +

+ + + + + Policy Content + + + Version History + + + Approvals + + + + +
+
+
+ + + +
+

+ Version history will be available soon. +

+
+
+ + +
+

+ Approval workflow will be available soon. +

+
+
+
-
- - - Policy Content - - -
-
+
+ + +

Policy Details

+ +
+
+
+ + Last Updated +
+

{formatDate(policy.updatedAt)}

+
+ +
+
+ + Review Due +
+

2025-02-15

+
+ +
+
+ + Owner +
+

Jane Smith, CISO

+
+ + + + + +

Danger Zone

+

+ Permanently delete this policy and all of its data. This action + cannot be undone. +

+ +
+
@@ -149,59 +269,91 @@ function PolicyOverviewPageContent({ function PolicyOverviewPageFallback() { return ( -
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+ + +
+
+
+
+
+
+
+
+
+
+
+ +
+ +
+
+
+
+
+ +
+
+
+
+
-
-
- - -
- -
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
-
- - -
- - -
-
-
-
-
-
+
+ + +
+ +
+
+
+
+
+ +
+
+
+
+ +
+
+
+
+ +
+ +
+
+
+
+
+
+
+ +
+ + + +
+
+
+ +
diff --git a/package-lock.json b/package-lock.json index d2bbf671d..ff63562bb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -30,6 +30,7 @@ "@radix-ui/react-select": "^2.0.0", "@radix-ui/react-separator": "^1.1.2", "@radix-ui/react-slot": "^1.1.2", + "@radix-ui/react-tabs": "^1.1.3", "@radix-ui/react-toast": "^1.2.6", "@radix-ui/react-tooltip": "^1.1.8", "class-variance-authority": "^0.7.1", @@ -45,7 +46,7 @@ "react-relay": "^18.2.0", "react-router": "^7.1.5", "relay-runtime": "^18.2.0", - "tailwind-merge": "^3.0.1", + "tailwind-merge": "^3.0.2", "tailwindcss-animate": "^1.0.7" }, "devDependencies": { @@ -3485,6 +3486,36 @@ } } }, + "node_modules/@radix-ui/react-tabs": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-tabs/-/react-tabs-1.1.3.tgz", + "integrity": "sha512-9mFyI30cuRDImbmFF6O2KUJdgEOsGh9Vmx9x/Dh9tOhL7BngmQPQfwW4aejKm5OHpfWIdmeV6ySyuxoOGjtNng==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.1", + "@radix-ui/react-context": "1.1.1", + "@radix-ui/react-direction": "1.1.0", + "@radix-ui/react-id": "1.1.0", + "@radix-ui/react-presence": "1.1.2", + "@radix-ui/react-primitive": "2.0.2", + "@radix-ui/react-roving-focus": "1.1.2", + "@radix-ui/react-use-controllable-state": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-toast": { "version": "1.2.6", "resolved": "https://registry.npmjs.org/@radix-ui/react-toast/-/react-toast-1.2.6.tgz", @@ -8478,9 +8509,9 @@ } }, "node_modules/tailwind-merge": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-3.0.1.tgz", - "integrity": "sha512-AvzE8FmSoXC7nC+oU5GlQJbip2UO7tmOhOfQyOmPhrStOGXHU08j8mZEHZ4BmCqY5dWTCo4ClWkNyRNx1wpT0g==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-3.0.2.tgz", + "integrity": "sha512-l7z+OYZ7mu3DTqrL88RiKrKIqO3NcpEO8V/Od04bNpvk0kiIFndGEoqfuzvj4yuhRkHKjRkII2z+KS2HfPcSxw==", "license": "MIT", "funding": { "type": "github",