diff --git a/.cursor/rules/design-system.mdc b/.cursor/rules/design-system.mdc new file mode 100644 index 0000000000..c754e14795 --- /dev/null +++ b/.cursor/rules/design-system.mdc @@ -0,0 +1,67 @@ +--- +description: +globs: *.tsx +alwaysApply: false +--- + +Rule Name: design-system +Description: +Design System & Component Guidelines + +## Design Philosophy + +- **B2B, Modern, Flat, Minimal, Elegant**: All UI should follow a clean, professional aesthetic suitable for business applications +- **Sleek & Minimal**: Avoid visual clutter, use whitespace effectively, keep interfaces clean +- **Dark Mode First**: Always ensure components work seamlessly in both light and dark modes + +## Component Usage + +- **Adhere to Base Components**: Minimize custom overrides and stick to shadcn/ui base components whenever possible +- **Semantic Color Classes**: Use semantic classes like `text-muted-foreground`, `bg-muted/50` instead of hardcoded colors +- **Dark Mode Support**: Always use dark mode variants like `bg-green-50 dark:bg-green-950/20`, `text-green-600 dark:text-green-400` + +## Typography & Sizing + +- **Moderate Text Sizes**: Avoid overly large text - prefer `text-base`, `text-sm`, `text-xs` over `text-xl+` +- **Consistent Hierarchy**: Use `font-medium`, `font-semibold` sparingly, prefer `font-normal` with size differentiation +- **Tabular Numbers**: Use `tabular-nums` class for numeric data to ensure proper alignment + +## Layout & Spacing + +- **Consistent Spacing**: Use standard Tailwind spacing scale (`space-y-4`, `gap-6`, etc.) +- **Card-Based Layouts**: Prefer Card components for content organization +- **Minimal Padding**: Use conservative padding - `p-3`, `p-4` rather than larger values +- **Clean Separators**: Use subtle borders (`border-t`, `border-muted`) instead of heavy dividers + +## Color & Visual Elements + +- **Status Colors**: + - Green for completed/success states + - Blue for in-progress/info states + - Yellow for warnings + - Red for errors/destructive actions +- **Subtle Indicators**: Use small colored dots (`w-2 h-2 rounded-full`) instead of large icons for status +- **Minimal Shadows**: Prefer `hover:shadow-sm` over heavy shadow effects +- **Progress Bars**: Keep thin (`h-1`, `h-2`) for minimal visual weight + +## Interactive Elements + +- **Subtle Hover States**: Use gentle transitions (`transition-shadow`, `hover:shadow-sm`) +- **Consistent Button Sizing**: Prefer `size="sm"` for most buttons, `size="icon"` for icon-only +- **Badge Usage**: Keep badges minimal with essential info only (percentages, short status) + +## Data Display + +- **Shared Design Language**: Ensure related components (cards, overviews, details) use consistent patterns +- **Minimal Stats**: Present data cleanly without excessive decoration +- **Contextual Icons**: Use small, relevant icons (`h-3 w-3`, `h-4 w-4`) sparingly for context + +## Anti-Patterns to Avoid + +- Large text sizes (`text-2xl+` except for main headings) +- Heavy shadows or borders +- Excessive use of colored backgrounds +- Redundant badges or status indicators +- Complex custom styling overrides +- Non-semantic color usage (hardcoded hex values) +- Cluttered layouts with too many visual elements diff --git a/apps/app/src/app/(app)/[orgId]/controls/[controlId]/components/PoliciesTable.tsx b/apps/app/src/app/(app)/[orgId]/controls/[controlId]/components/PoliciesTable.tsx index fd568afb08..63eb432ea4 100644 --- a/apps/app/src/app/(app)/[orgId]/controls/[controlId]/components/PoliciesTable.tsx +++ b/apps/app/src/app/(app)/[orgId]/controls/[controlId]/components/PoliciesTable.tsx @@ -2,12 +2,11 @@ import { DataTable } from "@/components/data-table/data-table"; import { DataTableColumnHeader } from "@/components/data-table/data-table-column-header"; -import { DataTableSortList } from "@/components/data-table/data-table-sort-list"; import { StatusIndicator } from "@/components/status-indicator"; import { useDataTable } from "@/hooks/use-data-table"; import { Policy } from "@comp/db/types"; -import { Card, CardContent, CardHeader, CardTitle } from "@comp/ui/card"; import { Input } from "@comp/ui/input"; +import { Icons } from "@comp/ui/icons"; import { ColumnDef } from "@tanstack/react-table"; import { useMemo, useState } from "react"; @@ -106,35 +105,23 @@ export function PoliciesTable({ }); return ( - - - - {"Linked Policies"} ({filteredPolicies.length}) - - - -
- setSearchTerm(e.target.value)} - className="max-w-sm" - /> - {/*
- -
*/} -
- row.id} - tableId={"policiesTable"} +
+
+ setSearchTerm(e.target.value)} + className="max-w-sm" + leftIcon={} /> - - +
+ + row.id} + tableId={"policiesTable"} + /> +
); } diff --git a/apps/app/src/app/(app)/[orgId]/controls/[controlId]/components/RequirementsTable.tsx b/apps/app/src/app/(app)/[orgId]/controls/[controlId]/components/RequirementsTable.tsx index 69e8f2d6ae..6f856f8b88 100644 --- a/apps/app/src/app/(app)/[orgId]/controls/[controlId]/components/RequirementsTable.tsx +++ b/apps/app/src/app/(app)/[orgId]/controls/[controlId]/components/RequirementsTable.tsx @@ -2,7 +2,6 @@ import { DataTable } from "@/components/data-table/data-table"; import { DataTableColumnHeader } from "@/components/data-table/data-table-column-header"; -import { DataTableSortList } from "@/components/data-table/data-table-sort-list"; import { useDataTable } from "@/hooks/use-data-table"; import type { FrameworkEditorFramework, @@ -10,8 +9,8 @@ import type { FrameworkInstance, RequirementMap, } from "@comp/db/types"; -import { Card, CardContent, CardHeader, CardTitle } from "@comp/ui/card"; import { Input } from "@comp/ui/input"; +import { Icons } from "@comp/ui/icons"; import { ColumnDef } from "@tanstack/react-table"; import { useMemo, useState } from "react"; @@ -21,8 +20,8 @@ interface RequirementsTableProps { orgId: string; } - export function RequirementsTable({ - requirements, +export function RequirementsTable({ + requirements, orgId, }: RequirementsTableProps) { const [searchTerm, setSearchTerm] = useState(""); @@ -41,9 +40,10 @@ interface RequirementsTableProps { title={"Name"} /> ), + cell: ({ row }) => { return ( - + {row.original.requirement.name} ); @@ -67,7 +67,7 @@ interface RequirementsTableProps { ), cell: ({ row }) => { return ( - + {row.original.requirement.description} ); @@ -115,41 +115,28 @@ interface RequirementsTableProps { }); return ( - - - - {"Linked Requirements"} ( - {filteredRequirements.length}) - - - -
- setSearchTerm(e.target.value)} - className="max-w-sm" - /> - {/*
- -
*/} -
- { - // This constructs the path to the specific requirement page - // row.requirementId is the FrameworkEditorRequirement.id (e.g. frk_rq_...) - // row.frameworkInstanceId is the ID of the FrameworkInstance - return `${row.frameworkInstanceId}/requirements/${row.requirementId}`; - }} - tableId={"r"} +
+
+ setSearchTerm(e.target.value)} + className="max-w-sm" + leftIcon={} /> - - +
+ + { + // This constructs the path to the specific requirement page + // row.requirementId is the FrameworkEditorRequirement.id (e.g. frk_rq_...) + // row.frameworkInstanceId is the ID of the FrameworkInstance + return `${row.frameworkInstanceId}/requirements/${row.requirementId}`; + }} + tableId={"r"} + /> +
); } diff --git a/apps/app/src/app/(app)/[orgId]/controls/[controlId]/components/SingleControl.tsx b/apps/app/src/app/(app)/[orgId]/controls/[controlId]/components/SingleControl.tsx index 4361d53f5a..8b82a00a5e 100644 --- a/apps/app/src/app/(app)/[orgId]/controls/[controlId]/components/SingleControl.tsx +++ b/apps/app/src/app/(app)/[orgId]/controls/[controlId]/components/SingleControl.tsx @@ -10,15 +10,15 @@ import type { RequirementMap, Task, } from "@comp/db/types"; -import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@comp/ui/card"; import { Button } from "@comp/ui/button"; +import { Tabs, TabsContent, TabsList, TabsTrigger } from "@comp/ui/tabs"; import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, } from "@comp/ui/dropdown-menu"; -import { MoreVertical, PencilIcon, Trash2 } from "lucide-react"; +import { MoreVertical, Trash2 } from "lucide-react"; import { useState } from "react"; import { ControlDeleteDialog } from "./ControlDeleteDialog"; import { useParams } from "next/navigation"; @@ -79,60 +79,92 @@ export function SingleControl({ return (
- - - -
-
- {control.name} -
-
- - - - - - - { - setDropdownOpen(false); - setDeleteDialogOpen(true); - }} - className="text-destructive focus:text-destructive" - > - - Delete - - - -
+ {/* Control Header */} +
+
+
+
+

{control.name}

+
- - - {control.description} - - - - - - + {control.description && ( +

+ {control.description} +

+ )} +
+ + + + + + { + setDropdownOpen(false); + setDeleteDialogOpen(true); + }} + className="text-destructive focus:text-destructive" + > + + Delete + + + +
+
+ + {/* Tabbed Content */} + + + + Requirements + + {control.requirementsMapped.length} + + + + Policies + + {relatedPolicies.length} + + + + Tasks + + {relatedTasks.length} + + + + + + + + + + + + + + + + {/* Delete Dialog */} setDeleteDialogOpen(false)} control={control} /> -
+
); } diff --git a/apps/app/src/app/(app)/[orgId]/controls/[controlId]/components/TasksTable.tsx b/apps/app/src/app/(app)/[orgId]/controls/[controlId]/components/TasksTable.tsx index 10306c98a2..5ed410d335 100644 --- a/apps/app/src/app/(app)/[orgId]/controls/[controlId]/components/TasksTable.tsx +++ b/apps/app/src/app/(app)/[orgId]/controls/[controlId]/components/TasksTable.tsx @@ -2,12 +2,11 @@ import { DataTable } from "@/components/data-table/data-table"; import { DataTableColumnHeader } from "@/components/data-table/data-table-column-header"; -import { DataTableSortList } from "@/components/data-table/data-table-sort-list"; import { StatusIndicator } from "@/components/status-indicator"; import { useDataTable } from "@/hooks/use-data-table"; -import { Task, Policy } from "@comp/db/types"; -import { Card, CardContent, CardHeader, CardTitle } from "@comp/ui/card"; +import { Task } from "@comp/db/types"; import { Input } from "@comp/ui/input"; +import { Icons } from "@comp/ui/icons"; import { ColumnDef } from "@tanstack/react-table"; import { useMemo, useState } from "react"; @@ -99,33 +98,23 @@ export function TasksTable({ tasks, orgId, controlId }: TasksTableProps) { }); return ( - - - Tasks ({filteredTasks.length}) - - -
- setSearchTerm(e.target.value)} - className="max-w-sm" - /> - {/*
- -
*/} -
- `/tasks/${row.id}`} - tableId={"t"} +
+
+ setSearchTerm(e.target.value)} + className="max-w-sm" + leftIcon={} /> - - +
+ + `/tasks/${row.id}`} + tableId={"t"} + /> +
); } diff --git a/apps/app/src/app/(app)/[orgId]/frameworks/[frameworkInstanceId]/components/FrameworkOverview.tsx b/apps/app/src/app/(app)/[orgId]/frameworks/[frameworkInstanceId]/components/FrameworkOverview.tsx index 49a88651bb..2cf8feda6e 100644 --- a/apps/app/src/app/(app)/[orgId]/frameworks/[frameworkInstanceId]/components/FrameworkOverview.tsx +++ b/apps/app/src/app/(app)/[orgId]/frameworks/[frameworkInstanceId]/components/FrameworkOverview.tsx @@ -2,7 +2,12 @@ import { Badge } from "@comp/ui/badge"; import { Button } from "@comp/ui/button"; -import { Card, CardContent, CardHeader, CardTitle } from "@comp/ui/card"; +import { + Card, + CardContent, + CardHeader, + CardTitle, +} from "@comp/ui/card"; import { DropdownMenu, DropdownMenuContent, @@ -11,8 +16,9 @@ import { } from "@comp/ui/dropdown-menu"; import { Progress } from "@comp/ui/progress"; import { Control, Task } from "@comp/db/types"; -import { MoreVertical, Trash2 } from "lucide-react"; +import { MoreVertical, Trash2, CheckCircle2, Clock, BarChart3, Target } from "lucide-react"; import { useState } from "react"; +import { cn } from "@comp/ui/cn"; import { getControlStatus } from "../../lib/utils"; import { FrameworkInstanceWithControls } from "../../types"; import { FrameworkDeleteDialog } from "./FrameworkDeleteDialog"; @@ -28,6 +34,7 @@ export function FrameworkOverview({ }: FrameworkOverviewProps) { const [deleteDialogOpen, setDeleteDialogOpen] = useState(false); const [dropdownOpen, setDropdownOpen] = useState(false); + // Get all controls from all requirements const allControls = frameworkInstanceWithControls.controls; const totalControls = allControls.length; @@ -45,65 +52,118 @@ export function FrameworkOverview({ ? Math.round((compliantControls / totalControls) * 100) : 0; + const getComplianceColor = (score: number) => { + if (score >= 80) return "text-green-600 dark:text-green-400"; + if (score >= 60) return "text-yellow-600 dark:text-yellow-400"; + return "text-red-600 dark:text-red-400"; + }; + + const getComplianceBadgeVariant = () => { + if (compliancePercentage >= 80) return "default"; + if (compliancePercentage >= 60) return "secondary"; + return "destructive"; + }; + + const inProgressControls = totalControls - compliantControls; + return ( -
- - - -
{frameworkInstanceWithControls.framework.name}
- - - - - - { - setDropdownOpen(false); - setDeleteDialogOpen(true); - }} - className="text-destructive focus:text-destructive" - > - - Delete - - - -
-
- -

- {frameworkInstanceWithControls.framework.description} - {" "} -

-
- - Framework ID:{" "} - {frameworkInstanceWithControls.frameworkId} +
+ {/* Framework Header */} +
+
+
+

+ {frameworkInstanceWithControls.framework.name} +

+ + {compliancePercentage}%
- - +

+ {frameworkInstanceWithControls.framework.description} +

+
+ + + + + + { + setDropdownOpen(false); + setDeleteDialogOpen(true); + }} + className="text-destructive focus:text-destructive" + > + + Delete Framework + + + +
- - - Compliance Progress - - -
- -

- {compliantControls} of {totalControls} controls - compliant -

-
-
-
+ {/* Compliance Dashboard */} +
+ {/* Progress Card */} + + + + + Compliance Progress + + + +
+
+
+ + {compliancePercentage} + + % complete +
+ +
+
+
+ {compliantControls} completed + {inProgressControls} remaining + {totalControls} total +
+
+
+ + {/* Stats Card */} + + + + + Control Status + + + +
+
+
+ Complete +
+ {compliantControls} +
+
+
+
+ In Progress +
+ {inProgressControls} +
+
+ Total + {totalControls} +
+
+
+
{/* Delete Dialog */} ), cell: ({ row }) => ( - + {row.original.name} ), @@ -76,7 +76,7 @@ export function FrameworkRequirements({ /> ), cell: ({ row }) => ( - + {row.original.description} ), @@ -125,14 +125,8 @@ export function FrameworkRequirements({ } return ( - - - - {"Requirements"} ( - {table.table.getFilteredRowModel().rows.length}) - - - +
+

Requirements ({table.table.getFilteredRowModel().rows.length})

*/} - - +
); } diff --git a/apps/app/src/app/(app)/[orgId]/frameworks/[frameworkInstanceId]/page.tsx b/apps/app/src/app/(app)/[orgId]/frameworks/[frameworkInstanceId]/page.tsx index 27dbe5059f..013de87d02 100644 --- a/apps/app/src/app/(app)/[orgId]/frameworks/[frameworkInstanceId]/page.tsx +++ b/apps/app/src/app/(app)/[orgId]/frameworks/[frameworkInstanceId]/page.tsx @@ -6,6 +6,7 @@ import PageWithBreadcrumb from "../../../../../components/pages/PageWithBreadcru import { getSingleFrameworkInstanceWithControls } from "../data/getSingleFrameworkInstanceWithControls"; import { FrameworkOverview } from "./components/FrameworkOverview"; import { FrameworkRequirements } from "./components/FrameworkRequirements"; +import { Separator } from "@comp/ui/separator"; interface PageProps { params: Promise<{ diff --git a/apps/app/src/app/(app)/[orgId]/frameworks/[frameworkInstanceId]/requirements/[requirementKey]/components/RequirementControls.tsx b/apps/app/src/app/(app)/[orgId]/frameworks/[frameworkInstanceId]/requirements/[requirementKey]/components/RequirementControls.tsx index 3a1e761ddd..bbd3a661bb 100644 --- a/apps/app/src/app/(app)/[orgId]/frameworks/[frameworkInstanceId]/requirements/[requirementKey]/components/RequirementControls.tsx +++ b/apps/app/src/app/(app)/[orgId]/frameworks/[frameworkInstanceId]/requirements/[requirementKey]/components/RequirementControls.tsx @@ -1,8 +1,6 @@ "use client"; import type { FrameworkEditorRequirement } from "@comp/db/types"; -import { Card, CardContent, CardHeader, CardTitle } from "@comp/ui/card"; -import { FrameworkInstanceWithControls } from "../../../../types"; import { RequirementControlsTable } from "./table/RequirementControlsTable"; import type { Control, RequirementMap, Task } from "@comp/db/types"; @@ -19,40 +17,32 @@ export function RequirementControls({ }: RequirementControlsProps) { return (
- - - -
- - {"Requirement"} - -

- {requirement.name} -

-
-
-
- -

+ {/* Requirement Header */} +

+

{requirement.name}

+ {requirement.description && ( +

{requirement.description}

- - + )} +
- - - - {"Controls"} ( - {relatedControls.length}) - - - - control.control)} - tasks={tasks} - /> - - + {/* Controls Section */} +
+
+
+

Controls

+ + {relatedControls.length} + +
+
+ + control.control)} + tasks={tasks} + /> +
); } diff --git a/apps/app/src/app/(app)/[orgId]/frameworks/[frameworkInstanceId]/requirements/[requirementKey]/page.tsx b/apps/app/src/app/(app)/[orgId]/frameworks/[frameworkInstanceId]/requirements/[requirementKey]/page.tsx index 37027b792d..e6f236cd8c 100644 --- a/apps/app/src/app/(app)/[orgId]/frameworks/[frameworkInstanceId]/requirements/[requirementKey]/page.tsx +++ b/apps/app/src/app/(app)/[orgId]/frameworks/[frameworkInstanceId]/requirements/[requirementKey]/page.tsx @@ -93,6 +93,8 @@ export default async function RequirementPage({ params }: PageProps) { console.log("relatedControls", relatedControls); + const maxLabelLength = 40; + return ( maxLabelLength ? `${currentRequirementDetails.name.slice(0, maxLabelLength)}...` : currentRequirementDetails.name, dropdown: siblingRequirementsDropdown, current: true, }, diff --git a/apps/app/src/app/(app)/[orgId]/frameworks/components/AddFrameworkModal.tsx b/apps/app/src/app/(app)/[orgId]/frameworks/components/AddFrameworkModal.tsx index 6485e6ea93..10dc8a6278 100644 --- a/apps/app/src/app/(app)/[orgId]/frameworks/components/AddFrameworkModal.tsx +++ b/apps/app/src/app/(app)/[orgId]/frameworks/components/AddFrameworkModal.tsx @@ -117,7 +117,7 @@ export function AddFrameworkModal({ key={frameworkId} htmlFor={`add-framework-${frameworkId}`} className={cn( - "relative flex flex-col p-4 border rounded-xs cursor-pointer transition-colors focus-within:ring-2 focus-within:ring-ring focus-within:ring-offset-2 w-full text-left", + "relative flex flex-col p-4 border rounded-sm cursor-pointer transition-colors focus-within:ring-2 focus-within:ring-ring focus-within:ring-offset-2 w-full text-left", field.value.includes(frameworkId) && "border-primary bg-primary/5", )} diff --git a/apps/app/src/app/(app)/[orgId]/frameworks/components/FrameworkCard.tsx b/apps/app/src/app/(app)/[orgId]/frameworks/components/FrameworkCard.tsx index 488f17c88f..988004a5bb 100644 --- a/apps/app/src/app/(app)/[orgId]/frameworks/components/FrameworkCard.tsx +++ b/apps/app/src/app/(app)/[orgId]/frameworks/components/FrameworkCard.tsx @@ -5,13 +5,12 @@ import { Badge } from "@comp/ui/badge"; import { Card, CardContent, - CardDescription, - CardFooter, CardHeader, CardTitle, } from "@comp/ui/card"; +import { Progress } from "@comp/ui/progress"; import { cn } from "@comp/ui/cn"; -import { ClipboardCheck, ClipboardList, Clock } from "lucide-react"; +import { BarChart3, Clock } from "lucide-react"; import Link from "next/link"; import { useParams } from "next/navigation"; import type { FrameworkInstanceWithControls } from "../types"; @@ -28,43 +27,35 @@ export function FrameworkCard({ tasks, }: FrameworkCardProps) { const { orgId } = useParams<{ orgId: string }>(); - const getComplianceColor = (score: number) => { - if (score >= 80) return "text-green-500"; - if (score >= 50) return "text-yellow-500"; - return "text-red-500"; - }; - - const getComplianceProgressColor = (score: number) => { - if (score >= 80) return "bg-green-500"; - if (score >= 50) return "bg-yellow-500"; - return "bg-red-500"; - }; const getStatusBadge = (score: number) => { if (score >= 95) return { label: "Compliant", - color: - "bg-green-100 text-green-800 dark:bg-green-900/30 dark:text-green-400", + variant: "default" as const, }; if (score >= 80) return { label: "Nearly Compliant", - color: - "bg-blue-100 text-blue-800 dark:bg-blue-900/30 dark:text-blue-400", + variant: "secondary" as const, }; if (score >= 50) return { label: "In Progress", - color: - "bg-yellow-100 text-yellow-800 dark:bg-yellow-900/30 dark:text-yellow-400", + variant: "outline" as const, }; return { label: "Needs Attention", - color: "bg-red-100 text-red-800 dark:bg-red-900/30 dark:text-red-400", + variant: "destructive" as const, }; }; + const getComplianceColor = (score: number) => { + if (score >= 80) return "text-green-600 dark:text-green-400"; + if (score >= 60) return "text-yellow-600 dark:text-yellow-400"; + return "text-red-600 dark:text-red-400"; + }; + const controlsCount = frameworkInstance.controls?.length || 0; const compliantControlsCount = Math.round( (complianceScore / 100) * controlsCount, @@ -111,94 +102,58 @@ export function FrameworkCard({ const statusBadge = getStatusBadge(complianceScore); // Calculate last activity date - use current date as fallback - const lastActivityDate = new Date().toISOString().slice(0, 10); + const lastActivityDate = new Date().toLocaleDateString(); return ( - - - - {frameworkDetails.name} - - {frameworkDetails.version} - - - -
-

+ + +

+
+ + {frameworkDetails.name} + +

{frameworkDetails.description}

- - {statusBadge.label} -
- + + {complianceScore}% + +
- -
-
-
- {"Status"} - - {complianceScore}% - -
-
-
+ + + {/* Progress Section */} +
+
+
+ + Progress
+ + {complianceScore}% +
+
-
-
-
- - {"Controls"} -
-

- {controlsCount} {"Tasks"} -

-
-
-
- - {"Completed"} -
-

- {compliantControlsCount} / {controlsCount} -

-
-
-
- - {"In Progress"} -
-

- {inProgressCount} / {controlsCount} -

-
+ {/* Stats */} +
+ {compliantControlsCount} complete + {inProgressCount} active + {controlsCount} total
- - -
- - {"Last Updated"}: {lastActivityDate} + + {/* Footer */} +
+ + Updated {lastActivityDate}
- + ); diff --git a/apps/app/src/app/(app)/[orgId]/frameworks/components/FrameworksOverview.tsx b/apps/app/src/app/(app)/[orgId]/frameworks/components/FrameworksOverview.tsx index 3bae5cf1a7..ebe808ed0a 100644 --- a/apps/app/src/app/(app)/[orgId]/frameworks/components/FrameworksOverview.tsx +++ b/apps/app/src/app/(app)/[orgId]/frameworks/components/FrameworksOverview.tsx @@ -10,6 +10,7 @@ import { useState } from "react"; import { AddFrameworkModal } from "./AddFrameworkModal"; import { useParams } from 'next/navigation'; import { Dialog } from "@comp/ui/dialog"; +import { PlusIcon } from "lucide-react"; export interface FrameworksOverviewProps { frameworksWithControls: FrameworkInstanceWithControls[]; @@ -31,17 +32,17 @@ export function FrameworksOverview({ return (
-
-

{"Frameworks"}

- -
+
+
+ +
{isAddFrameworkModalOpen && ( diff --git a/apps/app/src/app/(app)/[orgId]/layout.tsx b/apps/app/src/app/(app)/[orgId]/layout.tsx index e84a7698a8..dad159421b 100644 --- a/apps/app/src/app/(app)/[orgId]/layout.tsx +++ b/apps/app/src/app/(app)/[orgId]/layout.tsx @@ -62,7 +62,7 @@ export default async function Layout({ )}
{children} diff --git a/apps/app/src/app/(app)/[orgId]/people/all/loading.tsx b/apps/app/src/app/(app)/[orgId]/people/all/loading.tsx index 10038d06b5..fcb6e469a1 100644 --- a/apps/app/src/app/(app)/[orgId]/people/all/loading.tsx +++ b/apps/app/src/app/(app)/[orgId]/people/all/loading.tsx @@ -12,7 +12,7 @@ export default async function Loading() { return (
- + {"Members"} diff --git a/apps/app/src/app/(app)/[orgId]/people/all/page.tsx b/apps/app/src/app/(app)/[orgId]/people/all/page.tsx index 1bb2fd7842..9e4cd0b964 100644 --- a/apps/app/src/app/(app)/[orgId]/people/all/page.tsx +++ b/apps/app/src/app/(app)/[orgId]/people/all/page.tsx @@ -1,11 +1,13 @@ import type { Metadata } from "next"; import { TeamMembers } from "./components/TeamMembers"; +import { Card } from "@comp/ui/card"; +import PageCore from "@/components/pages/PageCore.tsx"; export default async function Members() { return ( -
+ -
+ ); } diff --git a/apps/app/src/app/(app)/[orgId]/policies/(overview)/components/policy-status-chart.tsx b/apps/app/src/app/(app)/[orgId]/policies/(overview)/components/policy-status-chart.tsx index 8befd474e8..c5e8a50810 100644 --- a/apps/app/src/app/(app)/[orgId]/policies/(overview)/components/policy-status-chart.tsx +++ b/apps/app/src/app/(app)/[orgId]/policies/(overview)/components/policy-status-chart.tsx @@ -47,7 +47,7 @@ const StatusTooltip = ({ active, payload }: any) => { if (active && payload && payload.length) { const data = payload[0].payload; return ( -
+

{data.name}

Count: {data.value} diff --git a/apps/app/src/app/(app)/[orgId]/policies/(overview)/page.tsx b/apps/app/src/app/(app)/[orgId]/policies/(overview)/page.tsx index 9caaca2058..1be8e7610b 100644 --- a/apps/app/src/app/(app)/[orgId]/policies/(overview)/page.tsx +++ b/apps/app/src/app/(app)/[orgId]/policies/(overview)/page.tsx @@ -12,7 +12,7 @@ export default async function PoliciesOverview() { return ( }> -

+
diff --git a/apps/app/src/app/(app)/[orgId]/policies/[policyId]/editor/components/PolicyHeader.tsx b/apps/app/src/app/(app)/[orgId]/policies/[policyId]/editor/components/PolicyHeader.tsx index d3a18e99bc..4c7b31e778 100644 --- a/apps/app/src/app/(app)/[orgId]/policies/[policyId]/editor/components/PolicyHeader.tsx +++ b/apps/app/src/app/(app)/[orgId]/policies/[policyId]/editor/components/PolicyHeader.tsx @@ -8,7 +8,7 @@ export function PolicyHeader({ saveStatus }: PolicyHeaderProps) { return (
-
+
{saveStatus}
diff --git a/apps/app/src/app/(app)/[orgId]/policies/[policyId]/layout.tsx b/apps/app/src/app/(app)/[orgId]/policies/[policyId]/layout.tsx deleted file mode 100644 index a246c90b7e..0000000000 --- a/apps/app/src/app/(app)/[orgId]/policies/[policyId]/layout.tsx +++ /dev/null @@ -1,29 +0,0 @@ -import { SecondaryMenu } from "@comp/ui/secondary-menu"; - -interface LayoutProps { - children: React.ReactNode; - params: Promise<{ policyId: string; orgId: string }>; -} - -export default async function Layout({ children, params }: LayoutProps) { - const { orgId, policyId } = await params; - - return ( -
- -
{children}
-
- ); -} diff --git a/apps/app/src/app/(app)/[orgId]/policies/all/layout.tsx b/apps/app/src/app/(app)/[orgId]/policies/all/layout.tsx deleted file mode 100644 index cb5ceb3a05..0000000000 --- a/apps/app/src/app/(app)/[orgId]/policies/all/layout.tsx +++ /dev/null @@ -1,29 +0,0 @@ -import { SecondaryMenu } from "@comp/ui/secondary-menu"; - -interface LayoutProps { - children: React.ReactNode; - params: Promise<{ policyId: string; orgId: string }>; -} - -export default async function Layout({ children, params }: LayoutProps) { - const { orgId } = await params; - - return ( -
- -
{children}
-
- ); -} diff --git a/apps/app/src/app/(app)/[orgId]/policies/(overview)/layout.tsx b/apps/app/src/app/(app)/[orgId]/policies/layout.tsx similarity index 90% rename from apps/app/src/app/(app)/[orgId]/policies/(overview)/layout.tsx rename to apps/app/src/app/(app)/[orgId]/policies/layout.tsx index cb5ceb3a05..88ce7fd65f 100644 --- a/apps/app/src/app/(app)/[orgId]/policies/(overview)/layout.tsx +++ b/apps/app/src/app/(app)/[orgId]/policies/layout.tsx @@ -9,7 +9,7 @@ export default async function Layout({ children, params }: LayoutProps) { const { orgId } = await params; return ( -
+
{"API Key"}

-
+
{createdApiKey}
@@ -207,7 +207,7 @@ export function CreateApiKeyDialog({ if (isDesktop) { return ( - +
@@ -216,7 +216,7 @@ export function CreateApiKeyDialog({ diff --git a/apps/app/src/app/(app)/[orgId]/settings/context-hub/page.tsx b/apps/app/src/app/(app)/[orgId]/settings/context-hub/page.tsx index e2358fbb3c..71aacf13e0 100644 --- a/apps/app/src/app/(app)/[orgId]/settings/context-hub/page.tsx +++ b/apps/app/src/app/(app)/[orgId]/settings/context-hub/page.tsx @@ -1,6 +1,7 @@ import type { Metadata } from "next"; import { ContextTable } from "./ContextTable"; import { getContextEntries } from "./data/getContextEntries"; +import PageCore from "@/components/pages/PageCore.tsx"; export default async function ContextHubSettings({ params, @@ -24,16 +25,18 @@ export default async function ContextHubSettings({ }); return ( - + + + ); } export async function generateMetadata(): Promise { return { - title: "Context Hub", + title: "Context", }; } \ No newline at end of file diff --git a/apps/app/src/app/(app)/[orgId]/settings/layout.tsx b/apps/app/src/app/(app)/[orgId]/settings/layout.tsx index 3a04e4b8d1..312666eb0c 100644 --- a/apps/app/src/app/(app)/[orgId]/settings/layout.tsx +++ b/apps/app/src/app/(app)/[orgId]/settings/layout.tsx @@ -35,22 +35,22 @@ export default async function Layout({ }, { path: `/${orgId}/settings/context-hub`, - label: "Context Hub", + label: "Context", }, { path: `/${orgId}/settings/api-keys`, label: "API", }, - { - path: `/${orgId}/settings/billing`, - label: "Billing", - enabled: false, - }, + // { + // path: `/${orgId}/settings/billing`, + // label: "Billing", + // enabled: false, + // }, ]} /> -
{children}
+
{children}
); } diff --git a/apps/app/src/app/(app)/[orgId]/settings/page.tsx b/apps/app/src/app/(app)/[orgId]/settings/page.tsx index 073e1c493e..8438198c7f 100644 --- a/apps/app/src/app/(app)/[orgId]/settings/page.tsx +++ b/apps/app/src/app/(app)/[orgId]/settings/page.tsx @@ -11,7 +11,7 @@ export default async function OrganizationSettings() { const organization = await organizationDetails(); return ( -
+
diff --git a/apps/app/src/app/(app)/[orgId]/settings/trust-portal/components/TrustPortalSwitch.tsx b/apps/app/src/app/(app)/[orgId]/settings/trust-portal/components/TrustPortalSwitch.tsx index 0d4247707a..ceb54cd243 100644 --- a/apps/app/src/app/(app)/[orgId]/settings/trust-portal/components/TrustPortalSwitch.tsx +++ b/apps/app/src/app/(app)/[orgId]/settings/trust-portal/components/TrustPortalSwitch.tsx @@ -502,19 +502,19 @@ function ComplianceFramework({ - + Started - + In Progress - + Compliant diff --git a/apps/app/src/app/(app)/[orgId]/settings/trust-portal/page.tsx b/apps/app/src/app/(app)/[orgId]/settings/trust-portal/page.tsx index 1a8b417038..acc0336164 100644 --- a/apps/app/src/app/(app)/[orgId]/settings/trust-portal/page.tsx +++ b/apps/app/src/app/(app)/[orgId]/settings/trust-portal/page.tsx @@ -11,7 +11,7 @@ export default async function TrustPortalSettings({ params }: { params: Promise< const trustPortal = await getTrustPortal(orgId); return ( -
+
+ setDeleteDialogOpen(false)} task={task} /> -
+ ); } diff --git a/apps/app/src/app/(app)/[orgId]/tasks/[taskId]/components/TaskBody.tsx b/apps/app/src/app/(app)/[orgId]/tasks/[taskId]/components/TaskBody.tsx index ba064e999d..e54beffd40 100644 --- a/apps/app/src/app/(app)/[orgId]/tasks/[taskId]/components/TaskBody.tsx +++ b/apps/app/src/app/(app)/[orgId]/tasks/[taskId]/components/TaskBody.tsx @@ -154,7 +154,7 @@ export function TaskBody({ return (
- - {/* Render Breadcrumbs only if we are in the tasks section */} - {task?.id && ( -
- - - - - Tasks - - - {taskId && ( - <> - - - {/* Render taskId as BreadcrumbPage since it's the current page */} - - {task?.title} - - - - )} - - -
- )} + {children} -
+ ); } diff --git a/apps/app/src/app/(app)/[orgId]/tasks/components/TaskFilterHeader.tsx b/apps/app/src/app/(app)/[orgId]/tasks/components/TaskFilterHeader.tsx index 0ff79462eb..785e2f0c51 100644 --- a/apps/app/src/app/(app)/[orgId]/tasks/components/TaskFilterHeader.tsx +++ b/apps/app/src/app/(app)/[orgId]/tasks/components/TaskFilterHeader.tsx @@ -47,9 +47,9 @@ export function TaskFilterHeader() { }; return ( -
+
{/* Status Filter Buttons */} -
+
- - {/* Action Filters (Clear) */} -
- {/* Conditionally render the 'Clear filters' button only when filters are active. */} - {filtersActive && ( + {/* Conditionally render the 'Clear filters' button only when filters are active. */} + {filtersActive && (
+ + {!isCollapsed && ( + Coming Soon + )} + - + Coming Soon + + )} + + +
+ ); + } + + return ( +
+ + + + + + {isCollapsed && ( + +
{item.name} {item.badge && ( - + {item.badge.text} )}
-
- - )} -
+ )} + + +
); }; type Props = { organizationId: string; - //userIsAdmin: boolean; isCollapsed?: boolean; onItemClick?: () => void; }; diff --git a/apps/app/src/components/mobile-menu.tsx b/apps/app/src/components/mobile-menu.tsx index d954675593..070b40cf66 100644 --- a/apps/app/src/components/mobile-menu.tsx +++ b/apps/app/src/components/mobile-menu.tsx @@ -56,7 +56,7 @@ export function MobileMenu({
- +
diff --git a/apps/app/src/components/organization-switcher.tsx b/apps/app/src/components/organization-switcher.tsx index 1a4f8cc65c..c37468939d 100644 --- a/apps/app/src/components/organization-switcher.tsx +++ b/apps/app/src/components/organization-switcher.tsx @@ -38,86 +38,36 @@ interface OrganizationSwitcherProps { interface OrganizationInitialsAvatarProps { name: string | null | undefined; - isCollapsed?: boolean; + size?: "sm" | "default"; className?: string; } const COLOR_PAIRS = [ - { - bg: "bg-sky-100 dark:bg-sky-900/70", - text: "text-sky-700 dark:text-sky-200", - }, - { - bg: "bg-blue-100 dark:bg-blue-900/70", - text: "text-blue-700 dark:text-blue-200", - }, - { - bg: "bg-indigo-100 dark:bg-indigo-900/70", - text: "text-indigo-700 dark:text-indigo-200", - }, - { - bg: "bg-purple-100 dark:bg-purple-900/70", - text: "text-purple-700 dark:text-purple-200", - }, - { - bg: "bg-fuchsia-100 dark:bg-fuchsia-900/70", - text: "text-fuchsia-700 dark:text-fuchsia-200", - }, - { - bg: "bg-pink-100 dark:bg-pink-900/70", - text: "text-pink-700 dark:text-pink-200", - }, - { - bg: "bg-rose-100 dark:bg-rose-900/70", - text: "text-rose-700 dark:text-rose-200", - }, - { - bg: "bg-red-100 dark:bg-red-900/70", - text: "text-red-700 dark:text-red-200", - }, - { - bg: "bg-orange-100 dark:bg-orange-900/70", - text: "text-orange-700 dark:text-orange-200", - }, - { - bg: "bg-amber-100 dark:bg-amber-900/70", - text: "text-amber-700 dark:text-amber-200", - }, - { - bg: "bg-yellow-100 dark:bg-yellow-900/70", - text: "text-yellow-700 dark:text-yellow-200", - }, - { - bg: "bg-lime-100 dark:bg-lime-900/70", - text: "text-lime-700 dark:text-lime-200", - }, - { - bg: "bg-green-100 dark:bg-green-900/70", - text: "text-green-700 dark:text-green-200", - }, - { - bg: "bg-emerald-100 dark:bg-emerald-900/70", - text: "text-emerald-700 dark:text-emerald-200", - }, - { - bg: "bg-teal-100 dark:bg-teal-900/70", - text: "text-teal-700 dark:text-teal-200", - }, - { - bg: "bg-cyan-100 dark:bg-cyan-900/70", - text: "text-cyan-700 dark:text-cyan-200", - }, + "bg-sky-100 text-sky-700 dark:bg-sky-900/70 dark:text-sky-200", + "bg-blue-100 text-blue-700 dark:bg-blue-900/70 dark:text-blue-200", + "bg-indigo-100 text-indigo-700 dark:bg-indigo-900/70 dark:text-indigo-200", + "bg-purple-100 text-purple-700 dark:bg-purple-900/70 dark:text-purple-200", + "bg-fuchsia-100 text-fuchsia-700 dark:bg-fuchsia-900/70 dark:text-fuchsia-200", + "bg-pink-100 text-pink-700 dark:bg-pink-900/70 dark:text-pink-200", + "bg-rose-100 text-rose-700 dark:bg-rose-900/70 dark:text-rose-200", + "bg-red-100 text-red-700 dark:bg-red-900/70 dark:text-red-200", + "bg-orange-100 text-orange-700 dark:bg-orange-900/70 dark:text-orange-200", + "bg-amber-100 text-amber-700 dark:bg-amber-900/70 dark:text-amber-200", + "bg-yellow-100 text-yellow-700 dark:bg-yellow-900/70 dark:text-yellow-200", + "bg-lime-100 text-lime-700 dark:bg-lime-900/70 dark:text-lime-200", + "bg-green-100 text-green-700 dark:bg-green-900/70 dark:text-green-200", + "bg-emerald-100 text-emerald-700 dark:bg-emerald-900/70 dark:text-emerald-200", + "bg-teal-100 text-teal-700 dark:bg-teal-900/70 dark:text-teal-200", + "bg-cyan-100 text-cyan-700 dark:bg-cyan-900/70 dark:text-cyan-200", ]; function OrganizationInitialsAvatar({ name, - isCollapsed, + size = "default", className, }: OrganizationInitialsAvatarProps) { const initials = name?.slice(0, 2).toUpperCase() || ""; - const sizeClasses = isCollapsed ? "h-8 w-8" : "h-8 w-8"; - const textSizeClass = isCollapsed ? "text-sm font-medium" : "text-xs"; - + let colorIndex = 0; if (initials.length > 0) { const charCodeSum = Array.from(initials).reduce( @@ -127,20 +77,18 @@ function OrganizationInitialsAvatar({ colorIndex = charCodeSum % COLOR_PAIRS.length; } - const selectedColorPair = COLOR_PAIRS[colorIndex] || COLOR_PAIRS[0]; + const selectedColorClass = COLOR_PAIRS[colorIndex] || COLOR_PAIRS[0]; return ( -
- - {initials} - + {initials}
); } @@ -219,44 +167,38 @@ export function OrganizationSwitcher({ > - {"Select Organization"} + Select Organization
- {"No results found"} + No results found {organizations.map((org) => ( {status === "executing" && pendingOrgId === org.id ? ( - + ) : currentOrganization?.id === org.id ? ( - + ) : ( - +
)} {getDisplayName(org)} @@ -294,11 +236,11 @@ export function OrganizationSwitcher({ setShowCreateOrg(true); setIsDialogOpen(false); }} - className="cursor-pointer" disabled={status === "executing"} + className="flex items-center gap-2" > - - {"Create Organization"} + + Create Organization diff --git a/apps/app/src/components/pages/PageCore.tsx.tsx b/apps/app/src/components/pages/PageCore.tsx.tsx new file mode 100644 index 0000000000..b32383247b --- /dev/null +++ b/apps/app/src/components/pages/PageCore.tsx.tsx @@ -0,0 +1,9 @@ +import { CardLiquidGlass } from "@comp/ui/card-liquid-glass"; + +export default function PageCore({ children }: { children: React.ReactNode }) { + return ( + + {children} + + ); +} diff --git a/apps/app/src/components/pages/PageWithBreadcrumb.tsx b/apps/app/src/components/pages/PageWithBreadcrumb.tsx index ecc5d7a8cb..25353831d1 100644 --- a/apps/app/src/components/pages/PageWithBreadcrumb.tsx +++ b/apps/app/src/components/pages/PageWithBreadcrumb.tsx @@ -16,6 +16,7 @@ import { import { ChevronDown } from "lucide-react"; import Link from "next/link"; import React from "react"; +import PageCore from "./PageCore.tsx"; interface BreadcrumbDropdownItem { label: string; @@ -37,12 +38,14 @@ interface PageLayoutProps { * @default 3 */ maxItems?: number; + maxLabelLength?: number; } export default function PageWithBreadcrumb({ children, breadcrumbs, maxItems = 3, + maxLabelLength = 40, }: PageLayoutProps) { const totalItems = breadcrumbs.length; const shouldCollapse = totalItems > maxItems; @@ -56,7 +59,7 @@ export default function PageWithBreadcrumb({ : []; return ( -
+ {visibleItems.map((item, index) => { @@ -74,17 +77,17 @@ export default function PageWithBreadcrumb({ > {item.current ? ( - {item.label} + {item.label.length > maxLabelLength ? `${item.label.slice(0, maxLabelLength)}...` : item.label} ) : ( <> - {item.label} + {item.label.length > maxLabelLength ? `${item.label.slice(0, maxLabelLength)}...` : item.label} )} - + {item.dropdown.map( (dropdownItem) => ( { - dropdownItem.label + dropdownItem.label.length > maxLabelLength ? `${dropdownItem.label.slice(0, maxLabelLength)}...` : dropdownItem.label } @@ -161,6 +164,6 @@ export default function PageWithBreadcrumb({ {children} -
+ ); } diff --git a/apps/app/src/components/sidebar-collapse-button.tsx b/apps/app/src/components/sidebar-collapse-button.tsx index 7f5422f317..f8d3f2a403 100644 --- a/apps/app/src/components/sidebar-collapse-button.tsx +++ b/apps/app/src/components/sidebar-collapse-button.tsx @@ -4,9 +4,8 @@ import { updateSidebarState } from "@/actions/sidebar"; import { useSidebar } from "@/context/sidebar-context"; import { Button } from "@comp/ui/button"; import { cn } from "@comp/ui/cn"; -import { Icons } from "@comp/ui/icons"; +import { ArrowLeftFromLine } from "lucide-react"; import { useAction } from "next-safe-action/hooks"; -import { useRouter } from "next/navigation"; interface SidebarCollapseButtonProps { isCollapsed: boolean; @@ -15,43 +14,38 @@ interface SidebarCollapseButtonProps { export function SidebarCollapseButton({ isCollapsed, }: SidebarCollapseButtonProps) { - const router = useRouter(); const { setIsCollapsed } = useSidebar(); const { execute } = useAction(updateSidebarState, { - onSuccess: () => { - router.refresh(); + onError: () => { + // Revert the optimistic update if the server action fails + setIsCollapsed(isCollapsed); }, }); const handleToggle = () => { // Update local state immediately for responsive UI setIsCollapsed(!isCollapsed); - // Update server state (cookie) + // Update server state (cookie) in the background execute({ isCollapsed: !isCollapsed }); }; - if (isCollapsed) { - return ( - - ); - } - return ( ); } diff --git a/apps/app/src/components/sidebar-logo.tsx b/apps/app/src/components/sidebar-logo.tsx index ec553bc896..7ec4da96bd 100644 --- a/apps/app/src/components/sidebar-logo.tsx +++ b/apps/app/src/components/sidebar-logo.tsx @@ -11,8 +11,8 @@ export function SidebarLogo({
diff --git a/apps/app/src/components/sidebar.tsx b/apps/app/src/components/sidebar.tsx index 72cfc10b5e..6db1a11d60 100644 --- a/apps/app/src/components/sidebar.tsx +++ b/apps/app/src/components/sidebar.tsx @@ -7,6 +7,7 @@ import { OrganizationSwitcher } from "./organization-switcher"; import { SidebarCollapseButton } from "./sidebar-collapse-button"; import { SidebarLogo } from "./sidebar-logo"; import { db } from "@comp/db"; +import { cn } from "@comp/ui/cn"; export async function Sidebar({ organization, @@ -26,14 +27,11 @@ export async function Sidebar({ }); return ( -
+
-
+
- {!isCollapsed && ( - - )} -
+
- {isCollapsed && ( +
- )} +
); diff --git a/apps/app/src/components/upload/FileCard.tsx b/apps/app/src/components/upload/FileCard.tsx index 3ae51c5ca6..c35acf1422 100644 --- a/apps/app/src/components/upload/FileCard.tsx +++ b/apps/app/src/components/upload/FileCard.tsx @@ -117,7 +117,7 @@ export function FileCard({
) : (
-
+
diff --git a/apps/app/src/jobs/lib/prompts.ts b/apps/app/src/jobs/lib/prompts.ts index 55c65fa8af..521825a099 100644 --- a/apps/app/src/jobs/lib/prompts.ts +++ b/apps/app/src/jobs/lib/prompts.ts @@ -3,27 +3,27 @@ import { logger } from "@trigger.dev/sdk/v3"; import { JSONContent } from "novel"; export const generatePrompt = ({ - policy, - existingPolicyContent, - contextHub, - companyName, - companyWebsite, + policy, + existingPolicyContent, + contextHub, + companyName, + companyWebsite, }: { - contextHub: string; - companyName: string; - companyWebsite: string; - policy: Policy; - existingPolicyContent: JSONContent | JSONContent[]; + contextHub: string; + companyName: string; + companyWebsite: string; + policy: Policy; + existingPolicyContent: JSONContent | JSONContent[]; }) => { - logger.info(`Generating prompt for policy ${policy.name}`); - logger.info(`Company Name: ${companyName}`); - logger.info(`Company Website: ${companyWebsite}`); - logger.info(`Context Hub: ${contextHub}`); - logger.info( - `Existing Policy Content: ${JSON.stringify(existingPolicyContent)}`, - ); - - return ` + logger.info(`Generating prompt for policy ${policy.name}`); + logger.info(`Company Name: ${companyName}`); + logger.info(`Company Website: ${companyWebsite}`); + logger.info(`Context: ${contextHub}`); + logger.info( + `Existing Policy Content: ${JSON.stringify(existingPolicyContent)}` + ); + + return ` Company details: Company Name: ${companyName} diff --git a/apps/app/src/lib/data-table.ts b/apps/app/src/lib/data-table.ts index f365c7567d..f91211bc5b 100644 --- a/apps/app/src/lib/data-table.ts +++ b/apps/app/src/lib/data-table.ts @@ -1,81 +1,46 @@ import type { - ExtendedColumnFilter, - FilterOperator, - FilterVariant, + ExtendedColumnFilter, + FilterOperator, + FilterVariant, } from "@/types/data-table"; -import type { Column } from "@tanstack/react-table"; import { dataTableConfig } from "./data-table-config"; -export function getCommonPinningStyles({ - column, - withBorder = false, -}: { - column: Column; - withBorder?: boolean; -}): React.CSSProperties { - const isPinned = column.getIsPinned(); - const isLastLeftPinnedColumn = - isPinned === "left" && column.getIsLastColumn("left"); - const isFirstRightPinnedColumn = - isPinned === "right" && column.getIsFirstColumn("right"); - - return { - boxShadow: withBorder - ? isLastLeftPinnedColumn - ? "-4px 0 4px -4px hsl(var(--border)) inset" - : isFirstRightPinnedColumn - ? "4px 0 4px -4px hsl(var(--border)) inset" - : undefined - : undefined, - left: isPinned === "left" ? `${column.getStart("left")}px` : undefined, - right: - isPinned === "right" ? `${column.getAfter("right")}px` : undefined, - opacity: isPinned ? 0.97 : 1, - position: isPinned ? "sticky" : "relative", - background: isPinned - ? "hsl(var(--background))" - : "hsl(var(--background))", - width: column.getSize(), - zIndex: isPinned ? 1 : 0, - }; -} - export function getFilterOperators(filterVariant: FilterVariant) { - const operatorMap: Record< - FilterVariant, - { label: string; value: FilterOperator }[] - > = { - text: dataTableConfig.textOperators, - number: dataTableConfig.numericOperators, - range: dataTableConfig.numericOperators, - date: dataTableConfig.dateOperators, - dateRange: dataTableConfig.dateOperators, - boolean: dataTableConfig.booleanOperators, - select: dataTableConfig.selectOperators, - multiSelect: dataTableConfig.multiSelectOperators, - }; - - return operatorMap[filterVariant] ?? dataTableConfig.textOperators; + const operatorMap: Record< + FilterVariant, + { label: string; value: FilterOperator }[] + > = { + text: dataTableConfig.textOperators, + number: dataTableConfig.numericOperators, + range: dataTableConfig.numericOperators, + date: dataTableConfig.dateOperators, + dateRange: dataTableConfig.dateOperators, + boolean: dataTableConfig.booleanOperators, + select: dataTableConfig.selectOperators, + multiSelect: dataTableConfig.multiSelectOperators, + }; + + return operatorMap[filterVariant] ?? dataTableConfig.textOperators; } export function getDefaultFilterOperator(filterVariant: FilterVariant) { - const operators = getFilterOperators(filterVariant); + const operators = getFilterOperators(filterVariant); - return operators[0]?.value ?? (filterVariant === "text" ? "iLike" : "eq"); + return operators[0]?.value ?? (filterVariant === "text" ? "iLike" : "eq"); } export function getValidFilters( - filters: ExtendedColumnFilter[], + filters: ExtendedColumnFilter[] ): ExtendedColumnFilter[] { - return filters.filter( - (filter) => - filter.operator === "isEmpty" || - filter.operator === "isNotEmpty" || - (Array.isArray(filter.value) - ? filter.value.length > 0 - : filter.value !== "" && - filter.value !== null && - filter.value !== undefined), - ); + return filters.filter( + (filter) => + filter.operator === "isEmpty" || + filter.operator === "isNotEmpty" || + (Array.isArray(filter.value) + ? filter.value.length > 0 + : filter.value !== "" && + filter.value !== null && + filter.value !== undefined) + ); } diff --git a/apps/app/src/styles/globals.css b/apps/app/src/styles/globals.css index 1c862cc02e..d0de04d8ae 100644 --- a/apps/app/src/styles/globals.css +++ b/apps/app/src/styles/globals.css @@ -20,3 +20,11 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; } + +.dark .textured-background { + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='250' height='30' viewBox='0 0 1000 120'%3E%3Cg fill='none' stroke='%23222' stroke-width='10' %3E%3Cpath d='M-500 75c0 0 125-30 250-30S0 75 0 75s125 30 250 30s250-30 250-30s125-30 250-30s250 30 250 30s125 30 250 30s250-30 250-30'/%3E%3Cpath d='M-500 45c0 0 125-30 250-30S0 45 0 45s125 30 250 30s250-30 250-30s125-30 250-30s250 30 250 30s125 30 250 30s250-30 250-30'/%3E%3Cpath d='M-500 105c0 0 125-30 250-30S0 105 0 105s125 30 250 30s250-30 250-30s125-30 250-30s250 30 250 30s125 30 250 30s250-30 250-30'/%3E%3Cpath d='M-500 15c0 0 125-30 250-30S0 15 0 15s125 30 250 30s250-30 250-30s125-30 250-30s250 30 250 30s125 30 250 30s250-30 250-30'/%3E%3Cpath d='M-500-15c0 0 125-30 250-30S0-15 0-15s125 30 250 30s250-30 250-30s125-30 250-30s250 30 250 30s125 30 250 30s250-30 250-30'/%3E%3Cpath d='M-500 135c0 0 125-30 250-30S0 135 0 135s125 30 250 30s250-30 250-30s125-30 250-30s250 30 250 30s125 30 250 30s250-30 250-30'/%3E%3C/g%3E%3C/svg%3E"); +} + +.textured-background { + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='250' height='30' viewBox='0 0 1000 120'%3E%3Cg fill='none' stroke='%23f5f5f5' stroke-width='10' %3E%3Cpath d='M-500 75c0 0 125-30 250-30S0 75 0 75s125 30 250 30s250-30 250-30s125-30 250-30s250 30 250 30s125 30 250 30s250-30 250-30'/%3E%3Cpath d='M-500 45c0 0 125-30 250-30S0 45 0 45s125 30 250 30s250-30 250-30s125-30 250-30s250 30 250 30s125 30 250 30s250-30 250-30'/%3E%3Cpath d='M-500 105c0 0 125-30 250-30S0 105 0 105s125 30 250 30s250-30 250-30s125-30 250-30s250 30 250 30s125 30 250 30s250-30 250-30'/%3E%3Cpath d='M-500 15c0 0 125-30 250-30S0 15 0 15s125 30 250 30s250-30 250-30s125-30 250-30s250 30 250 30s125 30 250 30s250-30 250-30'/%3E%3Cpath d='M-500-15c0 0 125-30 250-30S0-15 0-15s125 30 250 30s250-30 250-30s125-30 250-30s250 30 250 30s125 30 250 30s250-30 250-30'/%3E%3Cpath d='M-500 135c0 0 125-30 250-30S0 135 0 135s125 30 250 30s250-30 250-30s125-30 250-30s250 30 250 30s125 30 250 30s250-30 250-30'/%3E%3C/g%3E%3C/svg%3E"); +} diff --git a/apps/framework-editor/app/(pages)/controls/ControlsClientPage.tsx b/apps/framework-editor/app/(pages)/controls/ControlsClientPage.tsx index 2d5a877507..d78efba165 100644 --- a/apps/framework-editor/app/(pages)/controls/ControlsClientPage.tsx +++ b/apps/framework-editor/app/(pages)/controls/ControlsClientPage.tsx @@ -335,11 +335,11 @@ export function ControlsClientPage({ variant="outline" onClick={handleCancel} size="sm" - className="rounded-xs" + className="rounded-sm" > Cancel -
@@ -360,7 +360,7 @@ export function ControlsClientPage({ value={selectedFramework} onValueChange={setSelectedFramework} > - + diff --git a/apps/framework-editor/app/(pages)/policies/[policyId]/PolicyDetailsClientPage.tsx b/apps/framework-editor/app/(pages)/policies/[policyId]/PolicyDetailsClientPage.tsx index 64cb1fcaa7..14b08758da 100644 --- a/apps/framework-editor/app/(pages)/policies/[policyId]/PolicyDetailsClientPage.tsx +++ b/apps/framework-editor/app/(pages)/policies/[policyId]/PolicyDetailsClientPage.tsx @@ -40,7 +40,7 @@ export function PolicyDetailsClientPage({ return ( <> - +
@@ -58,7 +58,7 @@ export function PolicyDetailsClientPage({ variant="outline" size="sm" onClick={() => setIsEditDialogOpen(true)} - className="gap-1 rounded-xs" + className="gap-1 rounded-sm" > Edit Details @@ -67,7 +67,7 @@ export function PolicyDetailsClientPage({ variant="destructive" size="sm" onClick={() => setIsDeleteDialogOpen(true)} - className="gap-1 rounded-xs" + className="gap-1 rounded-sm" > Delete Policy diff --git a/apps/framework-editor/app/(pages)/policies/[policyId]/components/DeletePolicyDialog.tsx b/apps/framework-editor/app/(pages)/policies/[policyId]/components/DeletePolicyDialog.tsx index bad2d29a13..9dca4bf9db 100644 --- a/apps/framework-editor/app/(pages)/policies/[policyId]/components/DeletePolicyDialog.tsx +++ b/apps/framework-editor/app/(pages)/policies/[policyId]/components/DeletePolicyDialog.tsx @@ -52,7 +52,7 @@ export function DeletePolicyDialog({ return ( !open && onClose()}> - + Are you absolutely sure? @@ -62,13 +62,13 @@ export function DeletePolicyDialog({ - + Cancel {isPending ? "Deleting..." : "Delete Policy"} diff --git a/apps/framework-editor/app/(pages)/policies/[policyId]/components/EditPolicyDialog.tsx b/apps/framework-editor/app/(pages)/policies/[policyId]/components/EditPolicyDialog.tsx index 378677472e..2977455ab2 100644 --- a/apps/framework-editor/app/(pages)/policies/[policyId]/components/EditPolicyDialog.tsx +++ b/apps/framework-editor/app/(pages)/policies/[policyId]/components/EditPolicyDialog.tsx @@ -106,7 +106,7 @@ export function EditPolicyDialog({ return ( !open && onClose()}> - + Edit Policy Details @@ -122,7 +122,7 @@ export function EditPolicyDialog({ Name - + @@ -138,7 +138,7 @@ export function EditPolicyDialog({