Improve policy overview style
Signed-off-by: gearnode <bryan@frimin.fr>
This commit is contained in:
@@ -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": {
|
||||
|
||||
52
apps/console/src/components/ui/tabs.tsx
Normal file
52
apps/console/src/components/ui/tabs.tsx
Normal file
@@ -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<typeof TabsPrimitive.List>,
|
||||
React.ComponentPropsWithoutRef<typeof TabsPrimitive.List>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<TabsPrimitive.List
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"inline-flex h-10 items-center justify-center rounded-md bg-muted p-1 text-muted-foreground",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
TabsList.displayName = TabsPrimitive.List.displayName;
|
||||
|
||||
const TabsTrigger = React.forwardRef<
|
||||
React.ElementRef<typeof TabsPrimitive.Trigger>,
|
||||
React.ComponentPropsWithoutRef<typeof TabsPrimitive.Trigger>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<TabsPrimitive.Trigger
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"inline-flex items-center justify-center whitespace-nowrap rounded-sm px-3 py-1.5 text-sm font-medium ring-offset-background transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:bg-background data-[state=active]:text-foreground data-[state=active]:shadow-sm",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
TabsTrigger.displayName = TabsPrimitive.Trigger.displayName;
|
||||
|
||||
const TabsContent = React.forwardRef<
|
||||
React.ElementRef<typeof TabsPrimitive.Content>,
|
||||
React.ComponentPropsWithoutRef<typeof TabsPrimitive.Content>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<TabsPrimitive.Content
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"mt-2 ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
TabsContent.displayName = TabsPrimitive.Content.displayName;
|
||||
|
||||
export { Tabs, TabsList, TabsTrigger, TabsContent };
|
||||
@@ -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 (
|
||||
<Badge className="bg-green-100 text-green-700 hover:bg-green-200">
|
||||
Active
|
||||
</Badge>
|
||||
);
|
||||
case "DRAFT":
|
||||
return (
|
||||
<Badge className="bg-yellow-100 text-yellow-700 hover:bg-yellow-200">
|
||||
Draft
|
||||
</Badge>
|
||||
);
|
||||
default:
|
||||
return (
|
||||
<Badge className="bg-gray-100 text-gray-700 hover:bg-gray-200">
|
||||
{status}
|
||||
</Badge>
|
||||
);
|
||||
}
|
||||
// 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 (
|
||||
<div className="min-h-screen bg-background p-6 space-y-6">
|
||||
<div className="space-y-4 mb-8">
|
||||
<div className="flex justify-between items-center">
|
||||
<div>
|
||||
<h2 className="text-2xl font-semibold mb-1">{policy.name}</h2>
|
||||
</div>
|
||||
<div className="flex gap-2">
|
||||
<Button variant="outline" asChild>
|
||||
<Link
|
||||
to={`/organizations/${organizationId}/policies/${policy.id}/update`}
|
||||
>
|
||||
<Edit className="mr-2 h-4 w-4" />
|
||||
Edit Policy
|
||||
</Link>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 gap-6">
|
||||
<div>
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Policy Details</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="space-y-4">
|
||||
<div>
|
||||
<h4 className="text-sm font-medium text-muted-foreground mb-1">
|
||||
Status
|
||||
</h4>
|
||||
<div>{getStatusBadge(policy.status)}</div>
|
||||
<div className="container mx-auto py-6">
|
||||
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
|
||||
<div className="lg:col-span-2">
|
||||
<Card className="border shadow-sm">
|
||||
<CardContent className="p-6">
|
||||
<div className="flex items-center gap-3 mb-4">
|
||||
<div className="p-2 rounded-md bg-slate-100">
|
||||
<Shield className="h-6 w-6" />
|
||||
</div>
|
||||
<div>
|
||||
<h4 className="text-sm font-medium text-muted-foreground mb-1">
|
||||
Created
|
||||
</h4>
|
||||
<p>{formatDate(policy.createdAt)}</p>
|
||||
<div className="flex gap-2">
|
||||
<Badge
|
||||
variant="outline"
|
||||
className="bg-black text-white hover:bg-black/90 px-3 py-1 rounded-md font-medium"
|
||||
>
|
||||
SOC2
|
||||
</Badge>
|
||||
<Badge className="px-3 py-1 rounded-md font-medium">
|
||||
Security
|
||||
</Badge>
|
||||
{policy.status && (
|
||||
<Badge
|
||||
className={`px-3 py-1 rounded-md font-medium ${
|
||||
policy.status === "ACTIVE"
|
||||
? "bg-green-100 text-green-700 hover:bg-green-200"
|
||||
: policy.status === "DRAFT"
|
||||
? "bg-yellow-100 text-yellow-700 hover:bg-yellow-200"
|
||||
: "bg-gray-100 text-gray-700 hover:bg-gray-200"
|
||||
}`}
|
||||
>
|
||||
{policy.status === "ACTIVE"
|
||||
? "Active"
|
||||
: policy.status === "DRAFT"
|
||||
? "Draft"
|
||||
: policy.status}
|
||||
</Badge>
|
||||
)}
|
||||
</div>
|
||||
<div>
|
||||
<h4 className="text-sm font-medium text-muted-foreground mb-1">
|
||||
Last Updated
|
||||
</h4>
|
||||
<p>{formatDate(policy.updatedAt)}</p>
|
||||
<div className="ml-auto">
|
||||
<Button
|
||||
variant="outline"
|
||||
size="icon"
|
||||
className="rounded-full"
|
||||
>
|
||||
<Download className="h-5 w-5" />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h1 className="text-3xl font-bold mb-3">{policy.name}</h1>
|
||||
|
||||
<p className="text-muted-foreground mb-6">
|
||||
{getDescription(policy.content)}
|
||||
</p>
|
||||
|
||||
<Tabs
|
||||
defaultValue="content"
|
||||
value={activeTab}
|
||||
onValueChange={setActiveTab}
|
||||
className="mb-6"
|
||||
>
|
||||
<TabsList className="border-b w-full rounded-none bg-transparent p-0 h-auto">
|
||||
<TabsTrigger
|
||||
value="content"
|
||||
className={`rounded-none border-b-2 border-transparent px-4 py-2 font-medium ${
|
||||
activeTab === "content"
|
||||
? "border-primary text-primary"
|
||||
: "text-muted-foreground"
|
||||
}`}
|
||||
>
|
||||
Policy Content
|
||||
</TabsTrigger>
|
||||
<TabsTrigger
|
||||
value="history"
|
||||
className={`rounded-none border-b-2 border-transparent px-4 py-2 font-medium ${
|
||||
activeTab === "history"
|
||||
? "border-primary text-primary"
|
||||
: "text-muted-foreground"
|
||||
}`}
|
||||
>
|
||||
Version History
|
||||
</TabsTrigger>
|
||||
<TabsTrigger
|
||||
value="approvals"
|
||||
className={`rounded-none border-b-2 border-transparent px-4 py-2 font-medium ${
|
||||
activeTab === "approvals"
|
||||
? "border-primary text-primary"
|
||||
: "text-muted-foreground"
|
||||
}`}
|
||||
>
|
||||
Approvals
|
||||
</TabsTrigger>
|
||||
</TabsList>
|
||||
|
||||
<TabsContent value="content" className="pt-6">
|
||||
<div className="prose prose-sm md:prose-base lg:prose-lg max-w-none">
|
||||
<div
|
||||
className="policy-content"
|
||||
dangerouslySetInnerHTML={{ __html: policy.content || "" }}
|
||||
/>
|
||||
</div>
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="history" className="pt-6">
|
||||
<div className="text-center py-12">
|
||||
<p className="text-muted-foreground">
|
||||
Version history will be available soon.
|
||||
</p>
|
||||
</div>
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="approvals" className="pt-6">
|
||||
<div className="text-center py-12">
|
||||
<p className="text-muted-foreground">
|
||||
Approval workflow will be available soon.
|
||||
</p>
|
||||
</div>
|
||||
</TabsContent>
|
||||
</Tabs>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Policy Content</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="prose prose-sm md:prose-base lg:prose-lg max-w-none">
|
||||
<div
|
||||
className="policy-content"
|
||||
dangerouslySetInnerHTML={{ __html: policy.content || "" }}
|
||||
/>
|
||||
<div className="lg:col-span-1">
|
||||
<Card className="border shadow-sm mb-6">
|
||||
<CardContent className="p-6">
|
||||
<h2 className="text-xl font-semibold mb-6">Policy Details</h2>
|
||||
|
||||
<div className="space-y-6">
|
||||
<div>
|
||||
<div className="flex items-center gap-2 text-muted-foreground mb-1">
|
||||
<FileText className="h-4 w-4" />
|
||||
<span className="text-sm">Last Updated</span>
|
||||
</div>
|
||||
<p className="font-medium">{formatDate(policy.updatedAt)}</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div className="flex items-center gap-2 text-muted-foreground mb-1">
|
||||
<FileText className="h-4 w-4" />
|
||||
<span className="text-sm">Review Due</span>
|
||||
</div>
|
||||
<p className="font-medium">2025-02-15</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div className="flex items-center gap-2 text-muted-foreground mb-1">
|
||||
<User className="h-4 w-4" />
|
||||
<span className="text-sm">Owner</span>
|
||||
</div>
|
||||
<p className="font-medium">Jane Smith, CISO</p>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<Button asChild className="w-full">
|
||||
<Link
|
||||
to={`/organizations/${organizationId}/policies/${policy.id}/update`}
|
||||
>
|
||||
<Edit className="mr-2 h-4 w-4" />
|
||||
Edit Policy
|
||||
</Link>
|
||||
</Button>
|
||||
|
||||
<Card className="border shadow-sm mt-6 bg-red-50">
|
||||
<CardContent className="p-6">
|
||||
<h3 className="text-red-500 font-semibold mb-3">Danger Zone</h3>
|
||||
<p className="text-sm text-muted-foreground mb-4">
|
||||
Permanently delete this policy and all of its data. This action
|
||||
cannot be undone.
|
||||
</p>
|
||||
<Button variant="destructive" className="w-full">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="16"
|
||||
height="16"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
className="mr-2"
|
||||
>
|
||||
<path d="M3 6h18"></path>
|
||||
<path d="M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6"></path>
|
||||
<path d="M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2"></path>
|
||||
</svg>
|
||||
Delete Policy
|
||||
</Button>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -149,59 +269,91 @@ function PolicyOverviewPageContent({
|
||||
|
||||
function PolicyOverviewPageFallback() {
|
||||
return (
|
||||
<div className="min-h-screen bg-background p-6 space-y-6">
|
||||
<div className="space-y-4 mb-8">
|
||||
<div className="flex justify-between items-center">
|
||||
<div>
|
||||
<div className="h-8 w-48 bg-muted animate-pulse rounded mb-2" />
|
||||
<div className="h-4 w-96 bg-muted animate-pulse rounded" />
|
||||
</div>
|
||||
<div>
|
||||
<div className="h-10 w-32 bg-muted animate-pulse rounded" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="container mx-auto py-6">
|
||||
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
|
||||
<div className="lg:col-span-2">
|
||||
<Card className="border shadow-sm">
|
||||
<CardContent className="p-6">
|
||||
<div className="flex items-center gap-3 mb-4">
|
||||
<div className="p-2 rounded-md bg-slate-100/50 h-10 w-10 animate-pulse" />
|
||||
<div className="flex gap-2">
|
||||
<div className="h-6 w-16 bg-muted animate-pulse rounded-md" />
|
||||
<div className="h-6 w-20 bg-muted animate-pulse rounded-md" />
|
||||
<div className="h-6 w-16 bg-muted animate-pulse rounded-md" />
|
||||
</div>
|
||||
<div className="ml-auto">
|
||||
<div className="h-9 w-9 bg-muted animate-pulse rounded-full" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="h-8 w-3/4 bg-muted animate-pulse rounded mb-3" />
|
||||
|
||||
<div className="space-y-2 mb-6">
|
||||
<div className="h-4 w-full bg-muted animate-pulse rounded" />
|
||||
<div className="h-4 w-full bg-muted animate-pulse rounded" />
|
||||
<div className="h-4 w-2/3 bg-muted animate-pulse rounded" />
|
||||
</div>
|
||||
|
||||
<div className="flex border-b mb-6">
|
||||
<div className="px-4 py-2 h-8 w-28 bg-muted animate-pulse rounded" />
|
||||
<div className="px-4 py-2 h-8 w-28 bg-muted animate-pulse rounded opacity-50" />
|
||||
<div className="px-4 py-2 h-8 w-28 bg-muted animate-pulse rounded opacity-50" />
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 gap-6">
|
||||
<div>
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<div className="h-6 w-32 bg-muted animate-pulse rounded" />
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="space-y-4">
|
||||
<div>
|
||||
<div className="h-4 w-16 bg-muted animate-pulse rounded mb-2" />
|
||||
<div className="h-6 w-24 bg-muted animate-pulse rounded" />
|
||||
</div>
|
||||
<div>
|
||||
<div className="h-4 w-16 bg-muted animate-pulse rounded mb-2" />
|
||||
<div className="h-6 w-48 bg-muted animate-pulse rounded" />
|
||||
</div>
|
||||
<div>
|
||||
<div className="h-4 w-24 bg-muted animate-pulse rounded mb-2" />
|
||||
<div className="h-6 w-48 bg-muted animate-pulse rounded" />
|
||||
</div>
|
||||
<div className="h-4 w-full bg-muted animate-pulse rounded" />
|
||||
<div className="h-4 w-full bg-muted animate-pulse rounded" />
|
||||
<div className="h-4 w-full bg-muted animate-pulse rounded" />
|
||||
<div className="h-4 w-full bg-muted animate-pulse rounded" />
|
||||
<div className="h-4 w-3/4 bg-muted animate-pulse rounded" />
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<div className="h-6 w-32 bg-muted animate-pulse rounded" />
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="space-y-4">
|
||||
<div className="h-4 w-full bg-muted animate-pulse rounded" />
|
||||
<div className="h-4 w-full bg-muted animate-pulse rounded" />
|
||||
<div className="h-4 w-3/4 bg-muted animate-pulse rounded" />
|
||||
<div className="h-4 w-full bg-muted animate-pulse rounded" />
|
||||
<div className="h-4 w-5/6 bg-muted animate-pulse rounded" />
|
||||
<div className="lg:col-span-1">
|
||||
<Card className="border shadow-sm mb-6">
|
||||
<CardContent className="p-6">
|
||||
<div className="h-6 w-32 bg-muted animate-pulse rounded mb-6" />
|
||||
|
||||
<div className="space-y-6">
|
||||
<div>
|
||||
<div className="h-4 w-28 bg-muted animate-pulse rounded mb-1" />
|
||||
<div className="h-5 w-24 bg-muted animate-pulse rounded" />
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div className="h-4 w-28 bg-muted animate-pulse rounded mb-1" />
|
||||
<div className="h-5 w-24 bg-muted animate-pulse rounded" />
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div className="h-4 w-28 bg-muted animate-pulse rounded mb-1" />
|
||||
<div className="h-5 w-36 bg-muted animate-pulse rounded" />
|
||||
</div>
|
||||
|
||||
<hr className="my-4" />
|
||||
|
||||
<div>
|
||||
<div className="h-5 w-32 bg-muted animate-pulse rounded mb-3" />
|
||||
<div className="space-y-2">
|
||||
<div className="h-4 w-48 bg-muted animate-pulse rounded" />
|
||||
<div className="h-4 w-48 bg-muted animate-pulse rounded" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<div className="h-10 w-full bg-muted animate-pulse rounded mb-6" />
|
||||
|
||||
<Card className="border shadow-sm bg-red-50/30">
|
||||
<CardContent className="p-6">
|
||||
<div className="h-5 w-28 bg-muted animate-pulse rounded mb-3" />
|
||||
<div className="h-16 w-full bg-muted animate-pulse rounded mb-4" />
|
||||
<div className="h-10 w-full bg-muted animate-pulse rounded" />
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
39
package-lock.json
generated
39
package-lock.json
generated
@@ -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",
|
||||
|
||||
Reference in New Issue
Block a user