mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-20 11:37:06 +00:00
feature: migrate to v3 badge, additional badge stories, re-useable documentation link badge and misc styling fixes/improvements
This commit is contained in:
@@ -1,9 +1,11 @@
|
||||
import { components, OptionProps } from "react-select";
|
||||
import { faCheckCircle } from "@fortawesome/free-regular-svg-icons";
|
||||
import { faBuilding, faPlus } from "@fortawesome/free-solid-svg-icons";
|
||||
import { faPlus } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
|
||||
import { Badge, Tooltip } from "@app/components/v2";
|
||||
import { Tooltip } from "@app/components/v2";
|
||||
import { Badge, OrgIcon, SubOrgIcon } from "@app/components/v3";
|
||||
import { useOrganization } from "@app/context";
|
||||
import { TAvailableAppConnection } from "@app/hooks/api/appConnections";
|
||||
|
||||
export const AppConnectionOption = ({
|
||||
@@ -13,6 +15,8 @@ export const AppConnectionOption = ({
|
||||
}: OptionProps<TAvailableAppConnection>) => {
|
||||
const isCreateOption = props.data.id === "_create";
|
||||
|
||||
const { isSubOrganization } = useOrganization();
|
||||
|
||||
return (
|
||||
<components.Option isSelected={isSelected} {...props}>
|
||||
<div className="flex flex-row items-center justify-between">
|
||||
@@ -23,15 +27,22 @@ export const AppConnectionOption = ({
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
<p className="truncate">{children}</p>
|
||||
<p className="mr-auto truncate">{children}</p>
|
||||
{!props.data.projectId && (
|
||||
<Tooltip content="This connection belongs to your organization.">
|
||||
<div className="mr-auto ml-2">
|
||||
<Badge className="flex h-5 w-min items-center gap-1 bg-mineshaft-400/50 whitespace-nowrap text-bunker-300 hover:text-bunker-300">
|
||||
<FontAwesomeIcon icon={faBuilding} size="sm" />
|
||||
<Tooltip
|
||||
content={`This connection belongs to your ${isSubOrganization ? "sub-" : ""}organization.`}
|
||||
>
|
||||
{isSubOrganization ? (
|
||||
<Badge variant="sub-org">
|
||||
<SubOrgIcon />
|
||||
Sub-Organization
|
||||
</Badge>
|
||||
) : (
|
||||
<Badge variant="org">
|
||||
<OrgIcon />
|
||||
Organization
|
||||
</Badge>
|
||||
</div>
|
||||
)}
|
||||
</Tooltip>
|
||||
)}
|
||||
{isSelected && (
|
||||
|
||||
@@ -1,27 +1,20 @@
|
||||
import { ReactNode, useEffect, useMemo, useState } from "react";
|
||||
import {
|
||||
faCheck,
|
||||
faDownload,
|
||||
faTriangleExclamation,
|
||||
faXmark,
|
||||
IconDefinition
|
||||
} from "@fortawesome/free-solid-svg-icons";
|
||||
import { faXmark } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { differenceInSeconds } from "date-fns";
|
||||
import { twMerge } from "tailwind-merge";
|
||||
import { CheckIcon, DownloadIcon, LucideIcon, TriangleAlertIcon } from "lucide-react";
|
||||
|
||||
import { Badge, Tooltip } from "@app/components/v2";
|
||||
import { BadgeProps } from "@app/components/v2/Badge/Badge";
|
||||
import { Tooltip } from "@app/components/v2";
|
||||
import { Badge, TBadgeProps } from "@app/components/v3";
|
||||
import { PKI_SYNC_MAP } from "@app/helpers/pkiSyncs";
|
||||
import { PkiSyncStatus, TPkiSync } from "@app/hooks/api/pkiSyncs";
|
||||
|
||||
type Props = {
|
||||
pkiSync: TPkiSync;
|
||||
className?: string;
|
||||
mini?: boolean;
|
||||
};
|
||||
|
||||
export const PkiSyncImportStatusBadge = ({ pkiSync, className, mini }: Props) => {
|
||||
export const PkiSyncImportStatusBadge = ({ pkiSync, mini }: Props) => {
|
||||
const { importStatus, lastImportMessage, lastImportedAt, destination } = pkiSync;
|
||||
const [hide, setHide] = useState(importStatus === PkiSyncStatus.Succeeded);
|
||||
const destinationName = PKI_SYNC_MAP[destination].name;
|
||||
@@ -50,24 +43,24 @@ export const PkiSyncImportStatusBadge = ({ pkiSync, className, mini }: Props) =>
|
||||
|
||||
if (!importStatus || hide) return null;
|
||||
|
||||
let variant: BadgeProps["variant"];
|
||||
let variant: TBadgeProps["variant"];
|
||||
let label: string;
|
||||
let icon: IconDefinition;
|
||||
let Icon: LucideIcon;
|
||||
let tooltipContent: ReactNode;
|
||||
|
||||
switch (importStatus) {
|
||||
case PkiSyncStatus.Pending:
|
||||
case PkiSyncStatus.Running:
|
||||
variant = "primary";
|
||||
variant = "warning";
|
||||
label = "Importing Certificates...";
|
||||
tooltipContent = `Importing certificates from ${destinationName}. This may take a moment.`;
|
||||
icon = faDownload;
|
||||
Icon = DownloadIcon;
|
||||
|
||||
break;
|
||||
case PkiSyncStatus.Failed:
|
||||
variant = "danger";
|
||||
label = "Failed to Import Certificates";
|
||||
icon = faTriangleExclamation;
|
||||
Icon = TriangleAlertIcon;
|
||||
tooltipContent = (
|
||||
<div className="flex flex-col gap-2 py-1 whitespace-normal">
|
||||
{failureMessage && (
|
||||
@@ -93,20 +86,15 @@ export const PkiSyncImportStatusBadge = ({ pkiSync, className, mini }: Props) =>
|
||||
tooltipContent = "Successfully imported certificates.";
|
||||
variant = "success";
|
||||
label = "Certificates Imported";
|
||||
icon = faCheck;
|
||||
Icon = CheckIcon;
|
||||
}
|
||||
|
||||
return (
|
||||
<Tooltip position="bottom" className="max-w-sm" content={tooltipContent}>
|
||||
<div>
|
||||
<Badge
|
||||
className={twMerge("flex h-5 w-min items-center gap-1.5 whitespace-nowrap", className)}
|
||||
variant={variant}
|
||||
>
|
||||
<FontAwesomeIcon icon={icon} />
|
||||
{!mini && <span>{label}</span>}
|
||||
</Badge>
|
||||
</div>
|
||||
<Badge isSquare={mini} variant={variant}>
|
||||
<Icon />
|
||||
{!mini && label}
|
||||
</Badge>
|
||||
</Tooltip>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
import { faArrowUpRightFromSquare, faBookOpen } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
|
||||
import { DocumentationLinkBadge } from "@app/components/v3";
|
||||
import { PKI_SYNC_MAP } from "@app/helpers/pkiSyncs";
|
||||
import { PkiSync } from "@app/hooks/api/pkiSyncs";
|
||||
|
||||
@@ -20,23 +18,11 @@ export const PkiSyncModalHeader = ({ destination, isConfigured }: Props) => {
|
||||
className="h-12 w-12 rounded-md bg-bunker-500 p-2"
|
||||
/>
|
||||
<div>
|
||||
<div className="flex items-center text-mineshaft-300">
|
||||
<div className="flex items-center gap-x-2 text-mineshaft-300">
|
||||
{destinationDetails.name} Certificate Sync
|
||||
<a
|
||||
target="_blank"
|
||||
href={`https://infisical.com/docs/integrations/pki-syncs/${destination}`}
|
||||
className="mb-1 ml-1"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<div className="inline-block rounded-md bg-yellow/20 px-1.5 text-sm text-yellow opacity-80 hover:opacity-100">
|
||||
<FontAwesomeIcon icon={faBookOpen} className="mr-1 mb-[0.03rem] text-[12px]" />
|
||||
<span>Docs</span>
|
||||
<FontAwesomeIcon
|
||||
icon={faArrowUpRightFromSquare}
|
||||
className="mb-[0.07rem] ml-1 text-[10px]"
|
||||
/>
|
||||
</div>
|
||||
</a>
|
||||
<DocumentationLinkBadge
|
||||
href={`https://infisical.com/docs/documentation/platform/pki/certificate-syncs/${destination}`}
|
||||
/>
|
||||
</div>
|
||||
<p className="text-sm leading-4 text-mineshaft-400">
|
||||
{isConfigured
|
||||
|
||||
@@ -1,27 +1,20 @@
|
||||
import { ReactNode, useEffect, useMemo, useState } from "react";
|
||||
import {
|
||||
faCheck,
|
||||
faEraser,
|
||||
faTriangleExclamation,
|
||||
faXmark,
|
||||
IconDefinition
|
||||
} from "@fortawesome/free-solid-svg-icons";
|
||||
import { faXmark } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { differenceInSeconds } from "date-fns";
|
||||
import { twMerge } from "tailwind-merge";
|
||||
import { AlertTriangleIcon, CheckIcon, EraserIcon, LucideIcon } from "lucide-react";
|
||||
|
||||
import { Badge, Tooltip } from "@app/components/v2";
|
||||
import { BadgeProps } from "@app/components/v2/Badge/Badge";
|
||||
import { Tooltip } from "@app/components/v2";
|
||||
import { Badge, TBadgeProps } from "@app/components/v3";
|
||||
import { PKI_SYNC_MAP } from "@app/helpers/pkiSyncs";
|
||||
import { PkiSyncStatus, TPkiSync } from "@app/hooks/api/pkiSyncs";
|
||||
|
||||
type Props = {
|
||||
pkiSync: TPkiSync;
|
||||
className?: string;
|
||||
mini?: boolean;
|
||||
};
|
||||
|
||||
export const PkiSyncRemoveStatusBadge = ({ pkiSync, className, mini }: Props) => {
|
||||
export const PkiSyncRemoveStatusBadge = ({ pkiSync, mini }: Props) => {
|
||||
const { removeStatus, lastRemoveMessage, lastRemovedAt, destination } = pkiSync;
|
||||
const [hide, setHide] = useState(removeStatus === PkiSyncStatus.Succeeded);
|
||||
const destinationName = PKI_SYNC_MAP[destination].name;
|
||||
@@ -50,24 +43,24 @@ export const PkiSyncRemoveStatusBadge = ({ pkiSync, className, mini }: Props) =>
|
||||
|
||||
if (!removeStatus || hide) return null;
|
||||
|
||||
let variant: BadgeProps["variant"];
|
||||
let variant: TBadgeProps["variant"];
|
||||
let label: string;
|
||||
let icon: IconDefinition;
|
||||
let Icon: LucideIcon;
|
||||
let tooltipContent: ReactNode;
|
||||
|
||||
switch (removeStatus) {
|
||||
case PkiSyncStatus.Pending:
|
||||
case PkiSyncStatus.Running:
|
||||
variant = "primary";
|
||||
variant = "warning";
|
||||
label = "Removing Certificates...";
|
||||
tooltipContent = `Removing certificates from ${destinationName}. This may take a moment.`;
|
||||
icon = faEraser;
|
||||
Icon = EraserIcon;
|
||||
|
||||
break;
|
||||
case PkiSyncStatus.Failed:
|
||||
variant = "danger";
|
||||
label = "Failed to Remove Certificates";
|
||||
icon = faTriangleExclamation;
|
||||
Icon = AlertTriangleIcon;
|
||||
tooltipContent = (
|
||||
<div className="flex flex-col gap-2 py-1 whitespace-normal">
|
||||
{failureMessage && (
|
||||
@@ -93,20 +86,15 @@ export const PkiSyncRemoveStatusBadge = ({ pkiSync, className, mini }: Props) =>
|
||||
tooltipContent = "Successfully removed certificates.";
|
||||
variant = "success";
|
||||
label = "Certificates Removed";
|
||||
icon = faCheck;
|
||||
Icon = CheckIcon;
|
||||
}
|
||||
|
||||
return (
|
||||
<Tooltip position="bottom" className="max-w-sm" content={tooltipContent}>
|
||||
<div>
|
||||
<Badge
|
||||
className={twMerge("flex h-5 w-min items-center gap-1.5 whitespace-nowrap", className)}
|
||||
variant={variant}
|
||||
>
|
||||
<FontAwesomeIcon icon={icon} />
|
||||
{!mini && <span>{label}</span>}
|
||||
</Badge>
|
||||
</div>
|
||||
<Badge isSquare={mini} variant={variant}>
|
||||
<Icon />
|
||||
{!mini && label}
|
||||
</Badge>
|
||||
</Tooltip>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,55 +1,51 @@
|
||||
import {
|
||||
faCheck,
|
||||
faExclamationTriangle,
|
||||
faHourglass,
|
||||
faRotate,
|
||||
IconDefinition
|
||||
} from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
AlertTriangleIcon,
|
||||
CheckIcon,
|
||||
HourglassIcon,
|
||||
LucideIcon,
|
||||
RefreshCwIcon
|
||||
} from "lucide-react";
|
||||
|
||||
import { Badge, BadgeProps } from "@app/components/v2/Badge/Badge";
|
||||
import { Badge, TBadgeProps } from "@app/components/v3";
|
||||
import { PkiSyncStatus } from "@app/hooks/api/pkiSyncs";
|
||||
|
||||
type Props = {
|
||||
status: PkiSyncStatus;
|
||||
} & Omit<BadgeProps, "children" | "variant">;
|
||||
} & Omit<TBadgeProps, "children" | "variant">;
|
||||
|
||||
export const PkiSyncStatusBadge = ({ status }: Props) => {
|
||||
let variant: BadgeProps["variant"];
|
||||
let variant: TBadgeProps["variant"];
|
||||
let text: string;
|
||||
let icon: IconDefinition;
|
||||
let Icon: LucideIcon;
|
||||
|
||||
switch (status) {
|
||||
case PkiSyncStatus.Failed:
|
||||
variant = "danger";
|
||||
text = "Failed to Sync";
|
||||
icon = faExclamationTriangle;
|
||||
Icon = AlertTriangleIcon;
|
||||
break;
|
||||
case PkiSyncStatus.Succeeded:
|
||||
variant = "success";
|
||||
text = "Synced";
|
||||
icon = faCheck;
|
||||
Icon = CheckIcon;
|
||||
break;
|
||||
case PkiSyncStatus.Pending:
|
||||
variant = "primary";
|
||||
variant = "info";
|
||||
text = "Queued";
|
||||
icon = faHourglass;
|
||||
Icon = HourglassIcon;
|
||||
break;
|
||||
case PkiSyncStatus.Running:
|
||||
default:
|
||||
variant = "primary";
|
||||
variant = "info";
|
||||
text = "Syncing";
|
||||
icon = faRotate;
|
||||
Icon = RefreshCwIcon;
|
||||
break;
|
||||
}
|
||||
|
||||
return (
|
||||
<Badge className="flex h-5 w-min items-center gap-1.5 whitespace-nowrap" variant={variant}>
|
||||
<FontAwesomeIcon
|
||||
icon={icon}
|
||||
className={status === PkiSyncStatus.Running ? "animate-spin" : ""}
|
||||
/>
|
||||
<span>{text}</span>
|
||||
<Badge variant={variant}>
|
||||
<Icon className={status === PkiSyncStatus.Running ? "animate-spin" : ""} />
|
||||
{text}
|
||||
</Badge>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -2,7 +2,6 @@ import { faPlug, faRefresh, faTrash } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
|
||||
import {
|
||||
Badge,
|
||||
EmptyState,
|
||||
IconButton,
|
||||
Table,
|
||||
@@ -13,6 +12,7 @@ import {
|
||||
THead,
|
||||
Tr
|
||||
} from "@app/components/v2";
|
||||
import { Badge } from "@app/components/v3";
|
||||
import { TPkiSync } from "@app/hooks/api/pkiSyncs";
|
||||
|
||||
type Props = {
|
||||
@@ -29,10 +29,10 @@ const getSyncStatusBadge = (status?: string) => {
|
||||
case "FAILED":
|
||||
return <Badge variant="danger">Failed</Badge>;
|
||||
case "RUNNING":
|
||||
return <Badge variant="primary">Running</Badge>;
|
||||
return <Badge variant="warning">Running</Badge>;
|
||||
case "PENDING":
|
||||
default:
|
||||
return <Badge variant="primary">Pending</Badge>;
|
||||
return <Badge variant="warning">Pending</Badge>;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -71,7 +71,7 @@ export const PkiSyncTable = ({ pkiSyncs, onEdit, onDelete, onTrigger }: Props) =
|
||||
<Td>{pkiSync.name}</Td>
|
||||
<Td>
|
||||
<div className="flex items-center space-x-2">
|
||||
<Badge variant="primary">{pkiSync.destination}</Badge>
|
||||
<Badge variant="warning">{pkiSync.destination}</Badge>
|
||||
</div>
|
||||
</Td>
|
||||
<Td>{pkiSync.appConnectionName || "Unknown"}</Td>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { useFormContext } from "react-hook-form";
|
||||
|
||||
import { Badge, GenericFieldLabel } from "@app/components/v2";
|
||||
import { GenericFieldLabel } from "@app/components/v2";
|
||||
import { Badge } from "@app/components/v3";
|
||||
import { useProject } from "@app/context";
|
||||
import { PKI_SYNC_MAP } from "@app/helpers/pkiSyncs";
|
||||
import { useListWorkspacePkiSubscribers } from "@app/hooks/api";
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { faArrowUpRightFromSquare, faBookOpen } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { useNavigate, useRouterState } from "@tanstack/react-router";
|
||||
|
||||
import { SecretRotationV2Form } from "@app/components/secret-rotations-v2/forms";
|
||||
@@ -8,6 +6,7 @@ import { TSecretRotationV2Form } from "@app/components/secret-rotations-v2/forms
|
||||
import { SecretRotationV2ModalHeader } from "@app/components/secret-rotations-v2/SecretRotationV2ModalHeader";
|
||||
import { SecretRotationV2Select } from "@app/components/secret-rotations-v2/SecretRotationV2Select";
|
||||
import { Modal, ModalContent } from "@app/components/v2";
|
||||
import { DocumentationLinkBadge } from "@app/components/v3";
|
||||
import { ProjectEnv } from "@app/hooks/api/projects/types";
|
||||
import { SecretRotation, TSecretRotationV2 } from "@app/hooks/api/secretRotationsV2";
|
||||
|
||||
@@ -115,23 +114,9 @@ export const CreateSecretRotationV2Modal = ({ onOpenChange, isOpen, ...props }:
|
||||
selectedRotation ? (
|
||||
<SecretRotationV2ModalHeader isConfigured={false} type={selectedRotation} />
|
||||
) : (
|
||||
<div className="flex items-center text-mineshaft-300">
|
||||
<div className="flex items-center gap-x-2 text-mineshaft-300">
|
||||
Add Secret Rotation
|
||||
<a
|
||||
target="_blank"
|
||||
href="https://infisical.com/docs/documentation/platform/secret-rotation/overview"
|
||||
className="mb-1 ml-1"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<div className="inline-block rounded-md bg-yellow/20 px-1.5 text-sm text-yellow opacity-80 hover:opacity-100">
|
||||
<FontAwesomeIcon icon={faBookOpen} className="mr-1 mb-[0.03rem] text-[12px]" />
|
||||
<span>Docs</span>
|
||||
<FontAwesomeIcon
|
||||
icon={faArrowUpRightFromSquare}
|
||||
className="mb-[0.07rem] ml-1 text-[10px]"
|
||||
/>
|
||||
</div>
|
||||
</a>
|
||||
<DocumentationLinkBadge href="https://infisical.com/docs/documentation/platform/secret-rotation/overview" />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
import { faArrowUpRightFromSquare, faBookOpen } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
|
||||
import { DocumentationLinkBadge } from "@app/components/v3";
|
||||
import { SECRET_ROTATION_MAP } from "@app/helpers/secretRotationsV2";
|
||||
import { SecretRotation } from "@app/hooks/api/secretRotationsV2";
|
||||
|
||||
@@ -20,23 +18,11 @@ export const SecretRotationV2ModalHeader = ({ type, isConfigured }: Props) => {
|
||||
className="h-12 w-12 rounded-md bg-bunker-500 p-2"
|
||||
/>
|
||||
<div>
|
||||
<div className="flex items-center text-mineshaft-300">
|
||||
<div className="flex items-center gap-x-2 text-mineshaft-300">
|
||||
{destinationDetails.name} Rotation
|
||||
<a
|
||||
target="_blank"
|
||||
<DocumentationLinkBadge
|
||||
href={`https://infisical.com/docs/documentation/platform/secret-rotation/${type}`}
|
||||
className="mb-1 ml-1"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<div className="inline-block rounded-md bg-yellow/20 px-1.5 text-sm text-yellow opacity-80 hover:opacity-100">
|
||||
<FontAwesomeIcon icon={faBookOpen} className="mr-1 mb-[0.03rem] text-[12px]" />
|
||||
<span>Docs</span>
|
||||
<FontAwesomeIcon
|
||||
icon={faArrowUpRightFromSquare}
|
||||
className="mb-[0.07rem] ml-1 text-[10px]"
|
||||
/>
|
||||
</div>
|
||||
</a>
|
||||
/>
|
||||
</div>
|
||||
<p className="text-sm leading-4 text-mineshaft-400">
|
||||
{isConfigured
|
||||
|
||||
@@ -1,18 +1,17 @@
|
||||
import { faBan, faRotate, faXmark } from "@fortawesome/free-solid-svg-icons";
|
||||
import { faXmark } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { format, formatDistanceToNow } from "date-fns";
|
||||
import { twMerge } from "tailwind-merge";
|
||||
import { BanIcon, RefreshCwIcon, XIcon } from "lucide-react";
|
||||
|
||||
import { Tooltip } from "@app/components/v2";
|
||||
import { Badge } from "@app/components/v2/Badge/Badge";
|
||||
import { Badge } from "@app/components/v3";
|
||||
import { SecretRotationStatus, TSecretRotationV2 } from "@app/hooks/api/secretRotationsV2";
|
||||
|
||||
type Props = {
|
||||
secretRotation: TSecretRotationV2;
|
||||
className?: string;
|
||||
};
|
||||
|
||||
export const SecretRotationV2StatusBadge = ({ secretRotation, className }: Props) => {
|
||||
export const SecretRotationV2StatusBadge = ({ secretRotation }: Props) => {
|
||||
const { isAutoRotationEnabled, rotationStatus, nextRotationAt, lastRotationMessage } =
|
||||
secretRotation;
|
||||
|
||||
@@ -50,28 +49,18 @@ export const SecretRotationV2StatusBadge = ({ secretRotation, className }: Props
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<div>
|
||||
<Badge
|
||||
variant="danger"
|
||||
className={twMerge("flex h-5 w-min items-center gap-1.5 whitespace-nowrap", className)}
|
||||
>
|
||||
<FontAwesomeIcon icon={faXmark} />
|
||||
Rotation Failed
|
||||
</Badge>
|
||||
</div>
|
||||
<Badge variant="danger">
|
||||
<XIcon />
|
||||
Rotation Failed
|
||||
</Badge>
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
|
||||
if (!isAutoRotationEnabled) {
|
||||
return (
|
||||
<Badge
|
||||
className={twMerge(
|
||||
"flex h-5 w-min items-center gap-1.5 bg-mineshaft-400/50 whitespace-nowrap text-bunker-300",
|
||||
className
|
||||
)}
|
||||
>
|
||||
<FontAwesomeIcon icon={faBan} />
|
||||
<Badge variant="neutral">
|
||||
<BanIcon />
|
||||
Auto-Rotation Disabled
|
||||
</Badge>
|
||||
);
|
||||
@@ -92,20 +81,12 @@ export const SecretRotationV2StatusBadge = ({ secretRotation, className }: Props
|
||||
</>
|
||||
}
|
||||
>
|
||||
<div>
|
||||
<Badge
|
||||
variant={daysToRotation >= 7 ? "success" : "primary"}
|
||||
className={twMerge(
|
||||
"flex h-5 w-min items-center gap-1.5 whitespace-nowrap capitalize",
|
||||
className
|
||||
)}
|
||||
>
|
||||
<FontAwesomeIcon icon={faRotate} />
|
||||
{daysToRotation < 0
|
||||
? "Rotating"
|
||||
: `Rotates ${formatDistanceToNow(nextRotationAt, { addSuffix: true })}`}
|
||||
</Badge>
|
||||
</div>
|
||||
<Badge variant={daysToRotation >= 7 ? "info" : "warning"} className="capitalize">
|
||||
<RefreshCwIcon />
|
||||
{daysToRotation < 0
|
||||
? "Rotating"
|
||||
: `Rotates ${formatDistanceToNow(nextRotationAt, { addSuffix: true })}`}
|
||||
</Badge>
|
||||
</Tooltip>
|
||||
);
|
||||
};
|
||||
|
||||
+8
-5
@@ -1,8 +1,10 @@
|
||||
import { ReactNode } from "react";
|
||||
import { faArrowRight, faKey } from "@fortawesome/free-solid-svg-icons";
|
||||
import { faArrowRight } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { KeyIcon } from "lucide-react";
|
||||
|
||||
import { Badge, FormLabel } from "@app/components/v2";
|
||||
import { FormLabel } from "@app/components/v2";
|
||||
import { Badge } from "@app/components/v3";
|
||||
|
||||
type Props = {
|
||||
items: { name: string; input: ReactNode }[];
|
||||
@@ -32,9 +34,10 @@ export const SecretsMappingTable = ({ items }: Props) => {
|
||||
<tr key={name}>
|
||||
<td className="whitespace-nowrap">
|
||||
<div className="mb-4 flex h-full items-start justify-center">
|
||||
<Badge className="pointer-events-none flex h-[36px] w-full items-center justify-center gap-1.5 border border-mineshaft-600 bg-mineshaft-600 whitespace-nowrap text-bunker-200">
|
||||
<FontAwesomeIcon icon={faKey} />
|
||||
<span>{name}</span>
|
||||
{/* TODO(scott): probably shouldn't be a badge */}
|
||||
<Badge variant="neutral" className="h-[36px] w-full justify-center text-xs">
|
||||
<KeyIcon />
|
||||
{name}
|
||||
</Badge>
|
||||
</div>
|
||||
</td>
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { faArrowUpRightFromSquare, faBookOpen } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { useNavigate, useRouterState } from "@tanstack/react-router";
|
||||
|
||||
import { TSecretScanningDataSourceForm } from "@app/components/secret-scanning/forms/schemas";
|
||||
import { Modal, ModalContent } from "@app/components/v2";
|
||||
import { DocumentationLinkBadge } from "@app/components/v3";
|
||||
import {
|
||||
SecretScanningDataSource,
|
||||
TSecretScanningDataSource
|
||||
@@ -108,23 +107,9 @@ export const CreateSecretScanningDataSourceModal = ({ onOpenChange, isOpen, ...p
|
||||
selectedDataSource ? (
|
||||
<SecretScanningDataSourceModalHeader isConfigured={false} type={selectedDataSource} />
|
||||
) : (
|
||||
<div className="flex items-center text-mineshaft-300">
|
||||
<div className="flex items-center gap-x-2 text-mineshaft-300">
|
||||
Add Data Source
|
||||
<a
|
||||
target="_blank"
|
||||
href="https://infisical.com/docs/documentation/platform/secret-scanning/overview"
|
||||
className="mb-1 ml-1"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<div className="inline-block rounded-md bg-yellow/20 px-1.5 text-sm text-yellow opacity-80 hover:opacity-100">
|
||||
<FontAwesomeIcon icon={faBookOpen} className="mr-1 mb-[0.03rem] text-[12px]" />
|
||||
<span>Docs</span>
|
||||
<FontAwesomeIcon
|
||||
icon={faArrowUpRightFromSquare}
|
||||
className="mb-[0.07rem] ml-1 text-[10px]"
|
||||
/>
|
||||
</div>
|
||||
</a>
|
||||
<DocumentationLinkBadge href="https://infisical.com/docs/documentation/platform/secret-scanning/overview" />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
import { faArrowUpRightFromSquare, faBookOpen } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
|
||||
import { DocumentationLinkBadge } from "@app/components/v3";
|
||||
import { SECRET_SCANNING_DATA_SOURCE_MAP } from "@app/helpers/secretScanningV2";
|
||||
import { SecretScanningDataSource } from "@app/hooks/api/secretScanningV2";
|
||||
|
||||
@@ -20,23 +18,11 @@ export const SecretScanningDataSourceModalHeader = ({ type, isConfigured }: Prop
|
||||
className="h-12 rounded-md bg-bunker-500 p-2"
|
||||
/>
|
||||
<div>
|
||||
<div className="flex items-center text-mineshaft-300">
|
||||
<div className="flex items-center gap-x-2 text-mineshaft-300">
|
||||
{dataSourceDetails.name} Data Source
|
||||
<a
|
||||
target="_blank"
|
||||
<DocumentationLinkBadge
|
||||
href={`https://infisical.com/docs/documentation/platform/secret-scanning/${type}`}
|
||||
className="mb-1 ml-1"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<div className="inline-block rounded-md bg-yellow/20 px-1.5 text-sm text-yellow opacity-80 hover:opacity-100">
|
||||
<FontAwesomeIcon icon={faBookOpen} className="mr-1 mb-[0.03rem] text-[12px]" />
|
||||
<span>Docs</span>
|
||||
<FontAwesomeIcon
|
||||
icon={faArrowUpRightFromSquare}
|
||||
className="mb-[0.07rem] ml-1 text-[10px]"
|
||||
/>
|
||||
</div>
|
||||
</a>
|
||||
/>
|
||||
</div>
|
||||
<p className="text-sm leading-4 text-mineshaft-400">
|
||||
{isConfigured ? "Edit" : "Connect a"} {dataSourceDetails.name} Data Source
|
||||
|
||||
@@ -1,22 +1,23 @@
|
||||
import { faArrowRotateForward, faCheck, faXmark } from "@fortawesome/free-solid-svg-icons";
|
||||
import { faXmark } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { formatDistance } from "date-fns";
|
||||
import { twMerge } from "tailwind-merge";
|
||||
import { CheckIcon, RotateCwIcon, XIcon } from "lucide-react";
|
||||
|
||||
import { Badge, Tooltip } from "@app/components/v2";
|
||||
import { Tooltip } from "@app/components/v2";
|
||||
import { Badge } from "@app/components/v3";
|
||||
import { SecretScanningScanStatus } from "@app/hooks/api/secretScanningV2";
|
||||
|
||||
type Props = {
|
||||
status: SecretScanningScanStatus;
|
||||
statusMessage?: string | null;
|
||||
className?: string;
|
||||
|
||||
scannedAt?: string | null;
|
||||
};
|
||||
|
||||
export const SecretScanningScanStatusBadge = ({
|
||||
status,
|
||||
statusMessage,
|
||||
className,
|
||||
|
||||
scannedAt
|
||||
}: Props) => {
|
||||
if (status === SecretScanningScanStatus.Failed) {
|
||||
@@ -53,11 +54,8 @@ export const SecretScanningScanStatusBadge = ({
|
||||
}
|
||||
>
|
||||
<div>
|
||||
<Badge
|
||||
variant="danger"
|
||||
className={twMerge("flex h-5 w-min items-center gap-1.5 whitespace-nowrap", className)}
|
||||
>
|
||||
<FontAwesomeIcon icon={faXmark} />
|
||||
<Badge variant="danger">
|
||||
<XIcon />
|
||||
Scan Error
|
||||
</Badge>
|
||||
</div>
|
||||
@@ -67,26 +65,17 @@ export const SecretScanningScanStatusBadge = ({
|
||||
|
||||
if (status === SecretScanningScanStatus.Queued || status === SecretScanningScanStatus.Scanning) {
|
||||
return (
|
||||
<Badge
|
||||
className={twMerge("flex h-5 w-min items-center gap-1.5 whitespace-nowrap", className)}
|
||||
variant="primary"
|
||||
>
|
||||
<FontAwesomeIcon icon={faArrowRotateForward} className="animate-spin" />
|
||||
<span>Scanning</span>
|
||||
<Badge variant="info">
|
||||
<RotateCwIcon className="animate-spin" />
|
||||
Scanning
|
||||
</Badge>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Badge
|
||||
variant="success"
|
||||
className={twMerge(
|
||||
"flex h-5 w-min items-center gap-1.5 whitespace-nowrap capitalize",
|
||||
className
|
||||
)}
|
||||
>
|
||||
<FontAwesomeIcon icon={faCheck} />
|
||||
<span>Complete</span>
|
||||
<Badge variant="success">
|
||||
<CheckIcon />
|
||||
Complete
|
||||
</Badge>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,27 +1,20 @@
|
||||
import { ReactNode, useEffect, useMemo, useState } from "react";
|
||||
import {
|
||||
faCheck,
|
||||
faDownload,
|
||||
faTriangleExclamation,
|
||||
faXmark,
|
||||
IconDefinition
|
||||
} from "@fortawesome/free-solid-svg-icons";
|
||||
import { faXmark } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { differenceInSeconds } from "date-fns";
|
||||
import { twMerge } from "tailwind-merge";
|
||||
import { AlertTriangleIcon, CheckIcon, DownloadIcon, LucideIcon } from "lucide-react";
|
||||
|
||||
import { Badge, Tooltip } from "@app/components/v2";
|
||||
import { BadgeProps } from "@app/components/v2/Badge/Badge";
|
||||
import { Tooltip } from "@app/components/v2";
|
||||
import { Badge, TBadgeProps } from "@app/components/v3";
|
||||
import { SECRET_SYNC_MAP } from "@app/helpers/secretSyncs";
|
||||
import { SecretSyncStatus, TSecretSync } from "@app/hooks/api/secretSyncs";
|
||||
|
||||
type Props = {
|
||||
secretSync: TSecretSync;
|
||||
className?: string;
|
||||
mini?: boolean;
|
||||
};
|
||||
|
||||
export const SecretSyncImportStatusBadge = ({ secretSync, className, mini }: Props) => {
|
||||
export const SecretSyncImportStatusBadge = ({ secretSync, mini }: Props) => {
|
||||
const { importStatus, lastImportMessage, lastImportedAt, destination } = secretSync;
|
||||
const [hide, setHide] = useState(importStatus === SecretSyncStatus.Succeeded);
|
||||
const destinationName = SECRET_SYNC_MAP[destination].name;
|
||||
@@ -50,24 +43,24 @@ export const SecretSyncImportStatusBadge = ({ secretSync, className, mini }: Pro
|
||||
|
||||
if (!importStatus || hide) return null;
|
||||
|
||||
let variant: BadgeProps["variant"];
|
||||
let variant: TBadgeProps["variant"];
|
||||
let label: string;
|
||||
let icon: IconDefinition;
|
||||
let Icon: LucideIcon;
|
||||
let tooltipContent: ReactNode;
|
||||
|
||||
switch (importStatus) {
|
||||
case SecretSyncStatus.Pending:
|
||||
case SecretSyncStatus.Running:
|
||||
variant = "primary";
|
||||
variant = "warning";
|
||||
label = "Importing Secrets...";
|
||||
tooltipContent = `Importing secrets from ${destinationName}. This may take a moment.`;
|
||||
icon = faDownload;
|
||||
Icon = DownloadIcon;
|
||||
|
||||
break;
|
||||
case SecretSyncStatus.Failed:
|
||||
variant = "danger";
|
||||
label = "Failed to Import Secrets";
|
||||
icon = faTriangleExclamation;
|
||||
Icon = AlertTriangleIcon;
|
||||
tooltipContent = (
|
||||
<div className="flex flex-col gap-2 py-1 whitespace-normal">
|
||||
{failureMessage && (
|
||||
@@ -93,20 +86,15 @@ export const SecretSyncImportStatusBadge = ({ secretSync, className, mini }: Pro
|
||||
tooltipContent = "Successfully imported secrets.";
|
||||
variant = "success";
|
||||
label = "Secrets Imported";
|
||||
icon = faCheck;
|
||||
Icon = CheckIcon;
|
||||
}
|
||||
|
||||
return (
|
||||
<Tooltip position="bottom" className="max-w-sm" content={tooltipContent}>
|
||||
<div>
|
||||
<Badge
|
||||
className={twMerge("flex h-5 w-min items-center gap-1.5 whitespace-nowrap", className)}
|
||||
variant={variant}
|
||||
>
|
||||
<FontAwesomeIcon icon={icon} />
|
||||
{!mini && <span>{label}</span>}
|
||||
</Badge>
|
||||
</div>
|
||||
<Badge isSquare={mini} variant={variant}>
|
||||
<Icon />
|
||||
{!mini && label}
|
||||
</Badge>
|
||||
</Tooltip>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
import { faArrowUpRightFromSquare, faBookOpen } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
|
||||
import { DocumentationLinkBadge } from "@app/components/v3";
|
||||
import { SECRET_SYNC_MAP } from "@app/helpers/secretSyncs";
|
||||
import { SecretSync } from "@app/hooks/api/secretSyncs";
|
||||
|
||||
@@ -20,23 +18,11 @@ export const SecretSyncModalHeader = ({ destination, isConfigured }: Props) => {
|
||||
className="h-12 w-12 rounded-md bg-bunker-500 object-contain p-2"
|
||||
/>
|
||||
<div>
|
||||
<div className="flex items-center text-mineshaft-300">
|
||||
<div className="flex items-center gap-x-2 text-mineshaft-300">
|
||||
{destinationDetails.name} Sync
|
||||
<a
|
||||
target="_blank"
|
||||
<DocumentationLinkBadge
|
||||
href={`https://infisical.com/docs/integrations/secret-syncs/${destination}`}
|
||||
className="mb-1 ml-1"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<div className="inline-block rounded-md bg-yellow/20 px-1.5 text-sm text-yellow opacity-80 hover:opacity-100">
|
||||
<FontAwesomeIcon icon={faBookOpen} className="mr-1 mb-[0.03rem] text-[12px]" />
|
||||
<span>Docs</span>
|
||||
<FontAwesomeIcon
|
||||
icon={faArrowUpRightFromSquare}
|
||||
className="mb-[0.07rem] ml-1 text-[10px]"
|
||||
/>
|
||||
</div>
|
||||
</a>
|
||||
/>
|
||||
</div>
|
||||
<p className="text-sm leading-4 text-mineshaft-400">
|
||||
{isConfigured
|
||||
|
||||
@@ -1,27 +1,20 @@
|
||||
import { ReactNode, useEffect, useMemo, useState } from "react";
|
||||
import {
|
||||
faCheck,
|
||||
faEraser,
|
||||
faTriangleExclamation,
|
||||
faXmark,
|
||||
IconDefinition
|
||||
} from "@fortawesome/free-solid-svg-icons";
|
||||
import { faXmark } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { differenceInSeconds } from "date-fns";
|
||||
import { twMerge } from "tailwind-merge";
|
||||
import { AlertTriangleIcon, CheckIcon, EraserIcon, LucideIcon } from "lucide-react";
|
||||
|
||||
import { Badge, Tooltip } from "@app/components/v2";
|
||||
import { BadgeProps } from "@app/components/v2/Badge/Badge";
|
||||
import { Tooltip } from "@app/components/v2";
|
||||
import { Badge, TBadgeProps } from "@app/components/v3";
|
||||
import { SECRET_SYNC_MAP } from "@app/helpers/secretSyncs";
|
||||
import { SecretSyncStatus, TSecretSync } from "@app/hooks/api/secretSyncs";
|
||||
|
||||
type Props = {
|
||||
secretSync: TSecretSync;
|
||||
className?: string;
|
||||
mini?: boolean;
|
||||
};
|
||||
|
||||
export const SecretSyncRemoveStatusBadge = ({ secretSync, className, mini }: Props) => {
|
||||
export const SecretSyncRemoveStatusBadge = ({ secretSync, mini }: Props) => {
|
||||
const { removeStatus, lastRemoveMessage, lastRemovedAt, destination } = secretSync;
|
||||
const [hide, setHide] = useState(removeStatus === SecretSyncStatus.Succeeded);
|
||||
const destinationName = SECRET_SYNC_MAP[destination].name;
|
||||
@@ -50,24 +43,24 @@ export const SecretSyncRemoveStatusBadge = ({ secretSync, className, mini }: Pro
|
||||
|
||||
if (!removeStatus || hide) return null;
|
||||
|
||||
let variant: BadgeProps["variant"];
|
||||
let variant: TBadgeProps["variant"];
|
||||
let label: string;
|
||||
let icon: IconDefinition;
|
||||
let Icon: LucideIcon;
|
||||
let tooltipContent: ReactNode;
|
||||
|
||||
switch (removeStatus) {
|
||||
case SecretSyncStatus.Pending:
|
||||
case SecretSyncStatus.Running:
|
||||
variant = "primary";
|
||||
variant = "warning";
|
||||
label = "Removing Secrets...";
|
||||
tooltipContent = `Removing secrets from ${destinationName}. This may take a moment.`;
|
||||
icon = faEraser;
|
||||
Icon = EraserIcon;
|
||||
|
||||
break;
|
||||
case SecretSyncStatus.Failed:
|
||||
variant = "danger";
|
||||
label = "Failed to Remove Secrets";
|
||||
icon = faTriangleExclamation;
|
||||
Icon = AlertTriangleIcon;
|
||||
tooltipContent = (
|
||||
<div className="flex flex-col gap-2 py-1 whitespace-normal">
|
||||
{failureMessage && (
|
||||
@@ -93,20 +86,15 @@ export const SecretSyncRemoveStatusBadge = ({ secretSync, className, mini }: Pro
|
||||
tooltipContent = "Successfully removed secrets.";
|
||||
variant = "success";
|
||||
label = "Secrets Removed";
|
||||
icon = faCheck;
|
||||
Icon = CheckIcon;
|
||||
}
|
||||
|
||||
return (
|
||||
<Tooltip position="bottom" className="max-w-sm" content={tooltipContent}>
|
||||
<div>
|
||||
<Badge
|
||||
className={twMerge("flex h-5 w-min items-center gap-1.5 whitespace-nowrap", className)}
|
||||
variant={variant}
|
||||
>
|
||||
<FontAwesomeIcon icon={icon} />
|
||||
{!mini && <span>{label}</span>}
|
||||
</Badge>
|
||||
</div>
|
||||
<Badge isSquare={mini} variant={variant}>
|
||||
<Icon />
|
||||
{!mini && label}
|
||||
</Badge>
|
||||
</Tooltip>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,55 +1,51 @@
|
||||
import {
|
||||
faCheck,
|
||||
faExclamationTriangle,
|
||||
faHourglass,
|
||||
faRotate,
|
||||
IconDefinition
|
||||
} from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
AlertTriangleIcon,
|
||||
CheckIcon,
|
||||
HourglassIcon,
|
||||
LucideIcon,
|
||||
RefreshCwIcon
|
||||
} from "lucide-react";
|
||||
|
||||
import { Badge, BadgeProps } from "@app/components/v2/Badge/Badge";
|
||||
import { Badge, TBadgeProps } from "@app/components/v3";
|
||||
import { SecretSyncStatus } from "@app/hooks/api/secretSyncs";
|
||||
|
||||
type Props = {
|
||||
status: SecretSyncStatus;
|
||||
} & Omit<BadgeProps, "children" | "variant">;
|
||||
} & Omit<TBadgeProps, "children" | "variant">;
|
||||
|
||||
export const SecretSyncStatusBadge = ({ status }: Props) => {
|
||||
let variant: BadgeProps["variant"];
|
||||
let variant: TBadgeProps["variant"];
|
||||
let text: string;
|
||||
let icon: IconDefinition;
|
||||
let Icon: LucideIcon;
|
||||
|
||||
switch (status) {
|
||||
case SecretSyncStatus.Failed:
|
||||
variant = "danger";
|
||||
text = "Failed to Sync";
|
||||
icon = faExclamationTriangle;
|
||||
Icon = AlertTriangleIcon;
|
||||
break;
|
||||
case SecretSyncStatus.Succeeded:
|
||||
variant = "success";
|
||||
text = "Synced";
|
||||
icon = faCheck;
|
||||
Icon = CheckIcon;
|
||||
break;
|
||||
case SecretSyncStatus.Pending:
|
||||
variant = "primary";
|
||||
variant = "info";
|
||||
text = "Queued";
|
||||
icon = faHourglass;
|
||||
Icon = HourglassIcon;
|
||||
break;
|
||||
case SecretSyncStatus.Running:
|
||||
default:
|
||||
variant = "primary";
|
||||
variant = "info";
|
||||
text = "Syncing";
|
||||
icon = faRotate;
|
||||
Icon = RefreshCwIcon;
|
||||
break;
|
||||
}
|
||||
|
||||
return (
|
||||
<Badge className="flex h-5 w-min items-center gap-1.5 whitespace-nowrap" variant={variant}>
|
||||
<FontAwesomeIcon
|
||||
icon={icon}
|
||||
className={[SecretSyncStatus.Running].includes(status) ? "animate-spin" : ""}
|
||||
/>
|
||||
<span>{text}</span>
|
||||
<Badge variant={variant}>
|
||||
<Icon className={[SecretSyncStatus.Running].includes(status) ? "animate-spin" : ""} />
|
||||
{text}
|
||||
</Badge>
|
||||
);
|
||||
};
|
||||
|
||||
+3
-12
@@ -5,14 +5,8 @@ import { faCircleInfo } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
|
||||
import { SecretSyncConnectionField } from "@app/components/secret-syncs/forms/SecretSyncConnectionField";
|
||||
import {
|
||||
Badge,
|
||||
FilterableSelect,
|
||||
FormControl,
|
||||
Select,
|
||||
SelectItem,
|
||||
Tooltip
|
||||
} from "@app/components/v2";
|
||||
import { FilterableSelect, FormControl, Select, SelectItem, Tooltip } from "@app/components/v2";
|
||||
import { Badge } from "@app/components/v3";
|
||||
import { GCP_SYNC_SCOPES } from "@app/helpers/secretSyncs";
|
||||
import {
|
||||
useGcpConnectionListProjectLocations,
|
||||
@@ -26,10 +20,7 @@ import { TSecretSyncForm } from "../schemas";
|
||||
|
||||
const formatOptionLabel = ({ displayName, locationId }: TGcpLocation) => (
|
||||
<div className="flex w-full flex-row items-center gap-1">
|
||||
<span>{displayName}</span>{" "}
|
||||
<Badge className="h-5 leading-5" variant="success">
|
||||
{locationId}
|
||||
</Badge>
|
||||
<span>{displayName}</span> <Badge variant="info">{locationId}</Badge>
|
||||
</div>
|
||||
);
|
||||
|
||||
|
||||
+3
-2
@@ -2,7 +2,8 @@ import { components, OptionProps, SingleValue } from "react-select";
|
||||
import { faCheckCircle } from "@fortawesome/free-regular-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
|
||||
import { Badge, FilterableSelect } from "@app/components/v2";
|
||||
import { FilterableSelect } from "@app/components/v2";
|
||||
import { Badge } from "@app/components/v3";
|
||||
import { AWS_REGIONS } from "@app/helpers/appConnections";
|
||||
|
||||
const Option = ({ isSelected, children, ...props }: OptionProps<(typeof AWS_REGIONS)[number]>) => {
|
||||
@@ -10,7 +11,7 @@ const Option = ({ isSelected, children, ...props }: OptionProps<(typeof AWS_REGI
|
||||
<components.Option isSelected={isSelected} {...props}>
|
||||
<div className="flex flex-row items-center justify-between">
|
||||
<p className="truncate">{children}</p>
|
||||
<Badge variant="success" className="mr-auto ml-1 cursor-pointer">
|
||||
<Badge variant="neutral" className="mr-auto ml-1">
|
||||
{props.data.slug}
|
||||
</Badge>
|
||||
{isSelected && (
|
||||
|
||||
+6
-8
@@ -1,10 +1,10 @@
|
||||
import { useFormContext } from "react-hook-form";
|
||||
import { faEye } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { EyeIcon } from "lucide-react";
|
||||
|
||||
import { GenericFieldLabel } from "@app/components/secret-syncs";
|
||||
import { TSecretSyncForm } from "@app/components/secret-syncs/forms/schemas";
|
||||
import { Badge, Table, TBody, Td, Th, THead, Tooltip, Tr } from "@app/components/v2";
|
||||
import { Table, TBody, Td, Th, THead, Tooltip, Tr } from "@app/components/v2";
|
||||
import { Badge } from "@app/components/v3";
|
||||
import { AWS_REGIONS } from "@app/helpers/appConnections";
|
||||
import { SecretSync } from "@app/hooks/api/secretSyncs";
|
||||
|
||||
@@ -41,11 +41,9 @@ export const AwsParameterStoreSyncOptionsReviewFields = () => {
|
||||
}
|
||||
>
|
||||
<div className="w-min">
|
||||
<Badge className="flex h-5 w-min items-center gap-1.5 bg-mineshaft-400/50 whitespace-nowrap text-bunker-300">
|
||||
<FontAwesomeIcon icon={faEye} />
|
||||
<span>
|
||||
{tags.length} Tag{tags.length > 1 ? "s" : ""}
|
||||
</span>
|
||||
<Badge variant="neutral">
|
||||
<EyeIcon />
|
||||
{tags.length} Tag{tags.length > 1 ? "s" : ""}
|
||||
</Badge>
|
||||
</div>
|
||||
</Tooltip>
|
||||
|
||||
+7
-9
@@ -1,10 +1,10 @@
|
||||
import { useFormContext } from "react-hook-form";
|
||||
import { faEye } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { EyeIcon } from "lucide-react";
|
||||
|
||||
import { GenericFieldLabel } from "@app/components/secret-syncs";
|
||||
import { TSecretSyncForm } from "@app/components/secret-syncs/forms/schemas";
|
||||
import { Badge, Table, TBody, Td, Th, THead, Tooltip, Tr } from "@app/components/v2";
|
||||
import { Table, TBody, Td, Th, THead, Tooltip, Tr } from "@app/components/v2";
|
||||
import { Badge } from "@app/components/v3";
|
||||
import { AWS_REGIONS } from "@app/helpers/appConnections";
|
||||
import { SecretSync } from "@app/hooks/api/secretSyncs";
|
||||
import { AwsSecretsManagerSyncMappingBehavior } from "@app/hooks/api/secretSyncs/types/aws-secrets-manager-sync";
|
||||
@@ -26,7 +26,7 @@ export const AwsSecretsManagerSyncReviewFields = () => {
|
||||
<>
|
||||
<GenericFieldLabel label="Region">
|
||||
{awsRegion?.name}
|
||||
<Badge className="ml-1" variant="success">
|
||||
<Badge className="ml-1" variant="info">
|
||||
{awsRegion?.slug}{" "}
|
||||
</Badge>
|
||||
</GenericFieldLabel>
|
||||
@@ -73,11 +73,9 @@ export const AwsSecretsManagerSyncOptionsReviewFields = () => {
|
||||
}
|
||||
>
|
||||
<div className="w-min">
|
||||
<Badge className="flex h-5 w-min items-center gap-1.5 bg-mineshaft-400/50 whitespace-nowrap text-bunker-300">
|
||||
<FontAwesomeIcon icon={faEye} />
|
||||
<span>
|
||||
{tags.length} Tag{tags.length > 1 ? "s" : ""}
|
||||
</span>
|
||||
<Badge variant="neutral">
|
||||
<EyeIcon />
|
||||
{tags.length} Tag{tags.length > 1 ? "s" : ""}
|
||||
</Badge>
|
||||
</div>
|
||||
</Tooltip>
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@ import { useFormContext } from "react-hook-form";
|
||||
|
||||
import { GenericFieldLabel } from "@app/components/secret-syncs";
|
||||
import { TSecretSyncForm } from "@app/components/secret-syncs/forms/schemas";
|
||||
import { Badge } from "@app/components/v2";
|
||||
import { Badge } from "@app/components/v3";
|
||||
import { SecretSync } from "@app/hooks/api/secretSyncs";
|
||||
import { RenderSyncScope } from "@app/hooks/api/secretSyncs/types/render-sync";
|
||||
|
||||
|
||||
+3
-3
@@ -5,7 +5,7 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
|
||||
import { GenericFieldLabel } from "@app/components/secret-syncs";
|
||||
import { TSecretSyncForm } from "@app/components/secret-syncs/forms/schemas";
|
||||
import { Badge } from "@app/components/v2";
|
||||
import { Badge } from "@app/components/v3";
|
||||
import { useProject } from "@app/context";
|
||||
import { SECRET_SYNC_INITIAL_SYNC_BEHAVIOR_MAP, SECRET_SYNC_MAP } from "@app/helpers/secretSyncs";
|
||||
import { SecretSync, useDuplicateDestinationCheck } from "@app/hooks/api/secretSyncs";
|
||||
@@ -224,7 +224,7 @@ export const SecretSyncReviewFields = () => {
|
||||
</div>
|
||||
<div className="flex flex-wrap gap-x-8 gap-y-2">
|
||||
<GenericFieldLabel label="Auto-Sync">
|
||||
<Badge variant={isAutoSyncEnabled ? "success" : "danger"}>
|
||||
<Badge variant={isAutoSyncEnabled ? "success" : "neutral"}>
|
||||
{isAutoSyncEnabled ? "Enabled" : "Disabled"}
|
||||
</Badge>
|
||||
</GenericFieldLabel>
|
||||
@@ -235,7 +235,7 @@ export const SecretSyncReviewFields = () => {
|
||||
{AdditionalSyncOptionsFieldsComponent}
|
||||
{disableSecretDeletion && (
|
||||
<GenericFieldLabel label="Secret Deletion">
|
||||
<Badge variant="primary">Disabled</Badge>
|
||||
<Badge variant="warning">Disabled</Badge>
|
||||
</GenericFieldLabel>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -1,47 +0,0 @@
|
||||
import { forwardRef } from "react";
|
||||
import { cva, VariantProps } from "cva";
|
||||
import { twMerge } from "tailwind-merge";
|
||||
|
||||
interface IProps {
|
||||
children: React.ReactNode;
|
||||
className?: string;
|
||||
onClick?: (e: React.MouseEvent<HTMLDivElement>) => void;
|
||||
}
|
||||
|
||||
const badgeVariants = cva(
|
||||
[
|
||||
"inline-block cursor-default rounded-md bg-yellow/20 px-1.5 py-0.5 text-xs text-yellow opacity-80 hover:opacity-100"
|
||||
],
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
primary: "bg-yellow/20 text-yellow",
|
||||
danger: "bg-red/20 text-red",
|
||||
success: "bg-green/20 text-green",
|
||||
org: "bg-org-v1/20 text-org-v1 [&_svg]:text-org-v1 flex items-center opacity-100 hover:bg-org-v1/10 [&_svg]:size-3 gap-x-1 w-min whitespace-nowrap",
|
||||
namespace:
|
||||
"bg-namespace-v1/20 text-namespace-v1 [&_svg]:text-namespace-v1 flex opacity-100 hover:bg-namespace-v1/10 items-center [&_svg]:size-3.5 gap-x-1.5 w-min whitespace-nowrap",
|
||||
project:
|
||||
"bg-primary/10 text-primary [&_svg]:text-primary opacity-100 hover:bg-primary/10 flex items-center [&_svg]:size-3 w-min gap-x-1.5 whitespace-nowrap",
|
||||
instance:
|
||||
"bg-mineshaft-200/20 text-mineshaft-200 [&_svg]:text-mineshaft-200 opacity-100 hover:bg-mineshaft-200/20 flex items-center [&_svg]:size-3 gap-x-1.5 w-min whitespace-nowrap"
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
export type BadgeProps = VariantProps<typeof badgeVariants> & IProps;
|
||||
|
||||
export const Badge = forwardRef<HTMLDivElement, BadgeProps>(
|
||||
({ children, className, variant, ...props }, ref) => {
|
||||
return (
|
||||
<div
|
||||
className={twMerge(badgeVariants({ variant: variant || "primary" }), className)}
|
||||
{...props}
|
||||
ref={ref}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
);
|
||||
@@ -1 +0,0 @@
|
||||
export { Badge } from "./Badge";
|
||||
@@ -1,11 +1,16 @@
|
||||
import { IconDefinition } from "@fortawesome/free-brands-svg-icons";
|
||||
import { faCube, faCubes, faGlobe, faServer } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { createElement } from "react";
|
||||
import { ReactNode } from "@tanstack/react-router";
|
||||
import { LucideIcon } from "lucide-react";
|
||||
import { twMerge } from "tailwind-merge";
|
||||
|
||||
import { Badge } from "@app/components/v2";
|
||||
import { BadgeProps } from "@app/components/v2/Badge/Badge";
|
||||
import {
|
||||
Badge,
|
||||
InstanceIcon,
|
||||
OrgIcon,
|
||||
ProjectIcon,
|
||||
SubOrgIcon,
|
||||
TBadgeProps
|
||||
} from "@app/components/v3";
|
||||
import { ProjectType } from "@app/hooks/api/projects/types";
|
||||
|
||||
type Props = {
|
||||
@@ -16,19 +21,19 @@ type Props = {
|
||||
scope: "org" | "namespace" | "instance" | ProjectType | null;
|
||||
};
|
||||
|
||||
const SCOPE_NAME: Record<NonNullable<Props["scope"]>, { label: string; icon: IconDefinition }> = {
|
||||
org: { label: "Organization", icon: faGlobe },
|
||||
[ProjectType.SecretManager]: { label: "Project", icon: faCube },
|
||||
[ProjectType.CertificateManager]: { label: "Project", icon: faCube },
|
||||
[ProjectType.SSH]: { label: "Project", icon: faCube },
|
||||
[ProjectType.KMS]: { label: "Project", icon: faCube },
|
||||
[ProjectType.PAM]: { label: "Project", icon: faCube },
|
||||
[ProjectType.SecretScanning]: { label: "Project", icon: faCube },
|
||||
namespace: { label: "Sub-Organization", icon: faCubes },
|
||||
instance: { label: "Server", icon: faServer }
|
||||
const SCOPE_NAME: Record<NonNullable<Props["scope"]>, { label: string; icon: LucideIcon }> = {
|
||||
org: { label: "Organization", icon: OrgIcon },
|
||||
[ProjectType.SecretManager]: { label: "Project", icon: ProjectIcon },
|
||||
[ProjectType.CertificateManager]: { label: "Project", icon: ProjectIcon },
|
||||
[ProjectType.SSH]: { label: "Project", icon: ProjectIcon },
|
||||
[ProjectType.KMS]: { label: "Project", icon: ProjectIcon },
|
||||
[ProjectType.PAM]: { label: "Project", icon: ProjectIcon },
|
||||
[ProjectType.SecretScanning]: { label: "Project", icon: ProjectIcon },
|
||||
namespace: { label: "Sub-Organization", icon: SubOrgIcon },
|
||||
instance: { label: "Server", icon: InstanceIcon }
|
||||
};
|
||||
|
||||
const SCOPE_VARIANT: Record<NonNullable<Props["scope"]>, BadgeProps["variant"]> = {
|
||||
const SCOPE_VARIANT: Record<NonNullable<Props["scope"]>, TBadgeProps["variant"]> = {
|
||||
org: "org",
|
||||
[ProjectType.SecretManager]: "project",
|
||||
[ProjectType.CertificateManager]: "project",
|
||||
@@ -36,8 +41,8 @@ const SCOPE_VARIANT: Record<NonNullable<Props["scope"]>, BadgeProps["variant"]>
|
||||
[ProjectType.KMS]: "project",
|
||||
[ProjectType.PAM]: "project",
|
||||
[ProjectType.SecretScanning]: "project",
|
||||
namespace: "namespace",
|
||||
instance: "instance"
|
||||
namespace: "sub-org",
|
||||
instance: "neutral"
|
||||
};
|
||||
|
||||
export const PageHeader = ({ title, description, children, className, scope }: Props) => (
|
||||
@@ -47,7 +52,7 @@ export const PageHeader = ({ title, description, children, className, scope }: P
|
||||
<h1 className="text-3xl font-medium text-white capitalize">{title}</h1>
|
||||
{scope && (
|
||||
<Badge variant={SCOPE_VARIANT[scope]} className="mt-1 ml-2.5">
|
||||
<FontAwesomeIcon icon={SCOPE_NAME[scope].icon} />
|
||||
{createElement(SCOPE_NAME[scope].icon)}
|
||||
{SCOPE_NAME[scope].label}
|
||||
</Badge>
|
||||
)}
|
||||
|
||||
@@ -3,6 +3,7 @@ import { IconProp } from "@fortawesome/fontawesome-svg-core";
|
||||
import { faCaretDown, faCaretUp, faCheck } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import * as SelectPrimitive from "@radix-ui/react-select";
|
||||
import { LucideIcon } from "lucide-react";
|
||||
import { twMerge } from "tailwind-merge";
|
||||
|
||||
import { Spinner } from "../Spinner";
|
||||
@@ -19,6 +20,7 @@ type Props = {
|
||||
icon?: IconProp;
|
||||
isMulti?: boolean;
|
||||
iconClassName?: string;
|
||||
LucideIcon?: LucideIcon;
|
||||
dropdownContainerStyle?: React.CSSProperties;
|
||||
side?: SelectPrimitive.SelectContentProps["side"];
|
||||
};
|
||||
@@ -39,6 +41,7 @@ export const Select = forwardRef<HTMLButtonElement, SelectProps>(
|
||||
iconClassName,
|
||||
dropdownContainerStyle,
|
||||
side,
|
||||
LucideIcon: Lucide,
|
||||
...props
|
||||
},
|
||||
ref
|
||||
@@ -65,6 +68,7 @@ export const Select = forwardRef<HTMLButtonElement, SelectProps>(
|
||||
>
|
||||
<div className="flex items-center space-x-2 overflow-hidden text-ellipsis whitespace-nowrap">
|
||||
{props.icon && <FontAwesomeIcon icon={props.icon} className={iconClassName} />}
|
||||
{Lucide && <Lucide className={twMerge("size-3.5", iconClassName)} />}
|
||||
<div className="flex-1 truncate">
|
||||
<SelectPrimitive.Value placeholder={placeholder} />
|
||||
</div>
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
export * from "./AccessRestrictedBanner";
|
||||
export * from "./Accordion";
|
||||
export * from "./Alert";
|
||||
export * from "./Badge";
|
||||
export * from "./Breadcrumb";
|
||||
export * from "./Button";
|
||||
export * from "./Card";
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import type { Meta, StoryObj } from "@storybook/react-vite";
|
||||
import { Link } from "@tanstack/react-router";
|
||||
import {
|
||||
AlertTriangleIcon,
|
||||
AsteriskIcon,
|
||||
BanIcon,
|
||||
BoxesIcon,
|
||||
BoxIcon,
|
||||
CheckIcon,
|
||||
ChevronsUpDownIcon,
|
||||
CircleXIcon,
|
||||
@@ -11,9 +11,11 @@ import {
|
||||
GlobeIcon,
|
||||
InfoIcon,
|
||||
RadarIcon,
|
||||
TriangleAlertIcon
|
||||
TriangleAlertIcon,
|
||||
UserIcon
|
||||
} from "lucide-react";
|
||||
|
||||
import { OrgIcon, ProjectIcon, SubOrgIcon } from "../../platform";
|
||||
import { Badge } from "./Badge";
|
||||
|
||||
/**
|
||||
@@ -69,7 +71,29 @@ export const Neutral: Story = {
|
||||
parameters: {
|
||||
docs: {
|
||||
description: {
|
||||
story: "Use this variant when indicating neutral or disabled states."
|
||||
story:
|
||||
"Use this variant when indicating neutral or disabled states or when linking to external documents."
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const Ghost: Story = {
|
||||
name: "Variant: Ghost",
|
||||
args: {
|
||||
variant: "ghost",
|
||||
children: (
|
||||
<>
|
||||
<UserIcon />
|
||||
User
|
||||
</>
|
||||
)
|
||||
},
|
||||
parameters: {
|
||||
docs: {
|
||||
description: {
|
||||
story:
|
||||
"Use this variant when indicating a configuration or property value. Avoid using this variant as an interactive element as it is not intuitive to interact with."
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -109,8 +133,7 @@ export const Info: Story = {
|
||||
parameters: {
|
||||
docs: {
|
||||
description: {
|
||||
story:
|
||||
"Use this variant when indicating informational states or linking to external references."
|
||||
story: "Use this variant when indicating informational states."
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -162,7 +185,7 @@ export const Organization: Story = {
|
||||
variant: "org",
|
||||
children: (
|
||||
<>
|
||||
<GlobeIcon />
|
||||
<OrgIcon />
|
||||
Organization
|
||||
</>
|
||||
)
|
||||
@@ -182,7 +205,7 @@ export const SubOrganization: Story = {
|
||||
variant: "sub-org",
|
||||
children: (
|
||||
<>
|
||||
<BoxesIcon />
|
||||
<SubOrgIcon />
|
||||
Sub-Organization
|
||||
</>
|
||||
)
|
||||
@@ -202,7 +225,7 @@ export const Project: Story = {
|
||||
variant: "project",
|
||||
children: (
|
||||
<>
|
||||
<BoxIcon />
|
||||
<ProjectIcon />
|
||||
Project
|
||||
</>
|
||||
)
|
||||
@@ -283,10 +306,11 @@ export const AsButton: Story = {
|
||||
export const IsTruncatable: Story = {
|
||||
name: "Example: isTruncatable",
|
||||
args: {
|
||||
variant: "org",
|
||||
isTruncatable: true,
|
||||
children: (
|
||||
<>
|
||||
<GlobeIcon />
|
||||
<OrgIcon />
|
||||
<span>Infisical Infrastructure</span>
|
||||
</>
|
||||
)
|
||||
@@ -295,7 +319,7 @@ export const IsTruncatable: Story = {
|
||||
docs: {
|
||||
description: {
|
||||
story:
|
||||
"Use the `isTruncatable` prop with a `span` tag wrapping the text content to support truncation."
|
||||
"Use the `isTruncatable` prop with a `span` tag wrapping the text content to support truncation. Parent `div` should have a fixed width and `flex` class."
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -305,3 +329,48 @@ export const IsTruncatable: Story = {
|
||||
</div>
|
||||
)
|
||||
};
|
||||
|
||||
export const IsSquare: Story = {
|
||||
name: "Example: isSquare",
|
||||
args: {
|
||||
variant: "danger",
|
||||
isSquare: true,
|
||||
children: <AlertTriangleIcon />
|
||||
},
|
||||
parameters: {
|
||||
docs: {
|
||||
description: {
|
||||
story:
|
||||
"Use the `isSquare` prop when displaying a squared badge with 1-2 character text or only an icon."
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const IsFullWidth: Story = {
|
||||
name: "Example: isFullWidth",
|
||||
args: {
|
||||
variant: "neutral",
|
||||
isFullWidth: true,
|
||||
|
||||
children: (
|
||||
<>
|
||||
<AsteriskIcon />
|
||||
Secret Value
|
||||
</>
|
||||
)
|
||||
},
|
||||
parameters: {
|
||||
docs: {
|
||||
description: {
|
||||
story:
|
||||
"Use the `isFullWidth` prop to expand the badges width to fill it's parent container."
|
||||
}
|
||||
}
|
||||
},
|
||||
decorators: (Story) => (
|
||||
<div className="w-32">
|
||||
<Story />
|
||||
</div>
|
||||
)
|
||||
};
|
||||
|
||||
@@ -6,30 +6,37 @@ import { cn } from "@app/components/v3/utils";
|
||||
|
||||
const badgeVariants = cva(
|
||||
[
|
||||
"select-none items-center rounded-sm px-1.5 py-0.5 text-xs",
|
||||
"gap-x-1 [a&,button&]:cursor-pointer inline-flex",
|
||||
"[&>svg]:pointer-events-none [&>svg]:shrink-0 [&>svg]:stroke-[2.25] [&>svg]:size-3",
|
||||
"select-none items-center align-middle rounded-sm h-4.5 px-1.5 text-xs",
|
||||
"gap-x-1 [a&,button&]:cursor-pointer inline-flex font-normal",
|
||||
"[&>svg]:pointer-events-none [&>svg]:shrink-0 [&>svg]:stroke-[2.25] [&_svg:not([class*='size-'])]:size-3",
|
||||
"transition duration-200 ease-in-out"
|
||||
],
|
||||
{
|
||||
variants: {
|
||||
isTruncatable: {
|
||||
true: "[&>span,&>p]:truncate min-w-0",
|
||||
false: "w-fit shrink-0 whitespace-nowrap overflow-hidden"
|
||||
false: "w-fit shrink-0 min-w-fit whitespace-nowrap overflow-hidden"
|
||||
},
|
||||
isFullWidth: {
|
||||
true: "w-full justify-center"
|
||||
},
|
||||
isSquare: {
|
||||
true: "w-4.5 justify-center px-0.5"
|
||||
},
|
||||
variant: {
|
||||
neutral: "bg-neutral/30 text-neutral [a&,button&]:hover:bg-neutral/40",
|
||||
success: "bg-success/30 text-success [a&,button&]:hover:bg-success/40",
|
||||
info: "bg-info/30 text-info [a&,button&]:hover:bg-info/40",
|
||||
warning: "bg-warning/30 text-warning [a&,button&]:hover:bg-warning/40",
|
||||
danger: "bg-danger/30 text-danger [a&,button&]:hover:bg-danger/40",
|
||||
project: "bg-project/30 text-project [a&,button&]:hover:bg-project/40",
|
||||
org: "bg-org/30 text-org [a&,button&]:hover:bg-org/40",
|
||||
"sub-org": "bg-sub-org/30 text-sub-org [a&,button&]:hover:bg-sub-org/40"
|
||||
ghost: "text-mineshaft-200 gap-x-2",
|
||||
neutral: "bg-neutral/25 text-neutral [a&,button&]:hover:bg-neutral/35",
|
||||
success: "bg-success/25 text-success [a&,button&]:hover:bg-success/35",
|
||||
info: "bg-info/25 text-info [a&,button&]:hover:bg-info/35",
|
||||
warning: "bg-warning/25 text-warning [a&,button&]:hover:bg-warning/35",
|
||||
danger: "bg-danger/25 text-danger [a&,button&]:hover:bg-danger/35",
|
||||
project: "bg-project/25 text-project [a&,button&]:hover:bg-project/35",
|
||||
org: "bg-org/25 text-org [a&,button&]:hover:bg-org/35",
|
||||
"sub-org": "bg-sub-org/25 text-sub-org [a&,button&]:hover:bg-sub-org/35"
|
||||
}
|
||||
},
|
||||
defaultVariants: {
|
||||
variant: "success"
|
||||
variant: "neutral"
|
||||
}
|
||||
}
|
||||
);
|
||||
@@ -37,16 +44,28 @@ const badgeVariants = cva(
|
||||
type TBadgeProps = VariantProps<typeof badgeVariants> &
|
||||
React.ComponentProps<"span"> & {
|
||||
asChild?: boolean;
|
||||
variant: NonNullable<VariantProps<typeof badgeVariants>["variant"]>; // TODO: REMOVE
|
||||
};
|
||||
|
||||
const Badge = forwardRef<HTMLSpanElement, TBadgeProps>(
|
||||
({ className, variant, asChild = false, isTruncatable = false, ...props }, ref): JSX.Element => {
|
||||
(
|
||||
{
|
||||
className,
|
||||
variant,
|
||||
asChild = false,
|
||||
isTruncatable = false,
|
||||
isFullWidth = false,
|
||||
isSquare = false,
|
||||
...props
|
||||
},
|
||||
ref
|
||||
): JSX.Element => {
|
||||
const Comp = asChild ? Slot : "span";
|
||||
return (
|
||||
<Comp
|
||||
ref={ref}
|
||||
data-slot="badge"
|
||||
className={cn(badgeVariants({ variant, isTruncatable }), className)}
|
||||
className={cn(badgeVariants({ variant, isTruncatable, isFullWidth, isSquare }), className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
export * from "./generic";
|
||||
export * from "./platform";
|
||||
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
import type { Meta, StoryObj } from "@storybook/react-vite";
|
||||
|
||||
import { DocumentationLinkBadge } from "./DocumentationLinkBadge";
|
||||
|
||||
/**
|
||||
* The documentation link badge is a re-usable component to link to Infisical documentation references.
|
||||
*/
|
||||
const meta = {
|
||||
title: "Platform/Documentation Link Badge",
|
||||
component: DocumentationLinkBadge,
|
||||
parameters: {
|
||||
layout: "centered"
|
||||
},
|
||||
tags: ["autodocs"],
|
||||
argTypes: {},
|
||||
args: { href: "https://infisical.com/docs/documentation/getting-started/introduction" }
|
||||
} satisfies Meta<typeof DocumentationLinkBadge>;
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof meta>;
|
||||
|
||||
export const Default: Story = {
|
||||
name: "Example: Default"
|
||||
};
|
||||
@@ -0,0 +1,18 @@
|
||||
import { BookOpenIcon } from "lucide-react";
|
||||
|
||||
import { Badge } from "@app/components/v3";
|
||||
|
||||
type TDocumentationLinkBadgeProps = {
|
||||
href: string;
|
||||
};
|
||||
|
||||
export function DocumentationLinkBadge({ href }: TDocumentationLinkBadgeProps) {
|
||||
return (
|
||||
<Badge variant="neutral" asChild>
|
||||
<a href={href} target="_blank" rel="noopener noreferrer">
|
||||
<BookOpenIcon />
|
||||
Documentation
|
||||
</a>
|
||||
</Badge>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export * from "./DocumentationLinkBadge";
|
||||
@@ -0,0 +1,8 @@
|
||||
import { BoxesIcon, BoxIcon, Building2Icon, ServerIcon } from "lucide-react";
|
||||
|
||||
const InstanceIcon = ServerIcon;
|
||||
const OrgIcon = Building2Icon;
|
||||
const SubOrgIcon = BoxesIcon;
|
||||
const ProjectIcon = BoxIcon;
|
||||
|
||||
export { InstanceIcon, OrgIcon, ProjectIcon, SubOrgIcon };
|
||||
@@ -0,0 +1,2 @@
|
||||
export * from "./DocumentationLinkBadge";
|
||||
export * from "./ScopeIcons";
|
||||
Reference in New Issue
Block a user