mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-04 17:26:19 +00:00
feat: dashboard optimized on font awesome levels using symbols technique
This commit is contained in:
@@ -0,0 +1,19 @@
|
|||||||
|
import { forwardRef, HTMLAttributes } from "react";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
symbolName: string;
|
||||||
|
} & HTMLAttributes<HTMLDivElement>;
|
||||||
|
|
||||||
|
export const FontAwesomeSymbol = forwardRef<HTMLDivElement, Props>(
|
||||||
|
({ symbolName, ...props }, ref) => {
|
||||||
|
return (
|
||||||
|
<div ref={ref} {...props}>
|
||||||
|
<svg className="w-inherit h-inherit">
|
||||||
|
<use href={`#${symbolName}`} />
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
FontAwesomeSymbol.displayName = "FontAwesomeSymbol";
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export { FontAwesomeSymbol } from "./FontAwesomeSymbol";
|
||||||
@@ -8,6 +8,7 @@ import {
|
|||||||
DropdownMenuItem,
|
DropdownMenuItem,
|
||||||
DropdownMenuLabel,
|
DropdownMenuLabel,
|
||||||
DropdownMenuTrigger,
|
DropdownMenuTrigger,
|
||||||
|
FontAwesomeSymbol,
|
||||||
FormControl,
|
FormControl,
|
||||||
IconButton,
|
IconButton,
|
||||||
Input,
|
Input,
|
||||||
@@ -19,6 +20,7 @@ import {
|
|||||||
TextArea,
|
TextArea,
|
||||||
Tooltip
|
Tooltip
|
||||||
} from "@app/components/v2";
|
} from "@app/components/v2";
|
||||||
|
import { InfisicalSecretInput } from "@app/components/v2/InfisicalSecretInput";
|
||||||
import {
|
import {
|
||||||
ProjectPermissionActions,
|
ProjectPermissionActions,
|
||||||
ProjectPermissionSub,
|
ProjectPermissionSub,
|
||||||
@@ -29,20 +31,6 @@ import { useToggle } from "@app/hooks";
|
|||||||
import { DecryptedSecret } from "@app/hooks/api/secrets/types";
|
import { DecryptedSecret } from "@app/hooks/api/secrets/types";
|
||||||
import { WsTag } from "@app/hooks/api/types";
|
import { WsTag } from "@app/hooks/api/types";
|
||||||
import { subject } from "@casl/ability";
|
import { subject } from "@casl/ability";
|
||||||
import { faCheckCircle } from "@fortawesome/free-regular-svg-icons";
|
|
||||||
import {
|
|
||||||
faCheck,
|
|
||||||
faClock,
|
|
||||||
faClose,
|
|
||||||
faCodeBranch,
|
|
||||||
faComment,
|
|
||||||
faCopy,
|
|
||||||
faEllipsis,
|
|
||||||
faKey,
|
|
||||||
faTag,
|
|
||||||
faTags
|
|
||||||
} from "@fortawesome/free-solid-svg-icons";
|
|
||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
|
||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
import { AnimatePresence, motion } from "framer-motion";
|
import { AnimatePresence, motion } from "framer-motion";
|
||||||
import { memo, useEffect } from "react";
|
import { memo, useEffect } from "react";
|
||||||
@@ -50,7 +38,12 @@ import { Controller, useFieldArray, useForm } from "react-hook-form";
|
|||||||
import { twMerge } from "tailwind-merge";
|
import { twMerge } from "tailwind-merge";
|
||||||
|
|
||||||
import { CreateReminderForm } from "./CreateReminderForm";
|
import { CreateReminderForm } from "./CreateReminderForm";
|
||||||
import { formSchema, SecretActionType, TFormSchema } from "./SecretListView.utils";
|
import {
|
||||||
|
FontAwesomeSpriteName,
|
||||||
|
formSchema,
|
||||||
|
SecretActionType,
|
||||||
|
TFormSchema
|
||||||
|
} from "./SecretListView.utils";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
secret: DecryptedSecret;
|
secret: DecryptedSecret;
|
||||||
@@ -206,7 +199,6 @@ export const SecretItem = memo(
|
|||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<form onSubmit={handleSubmit(handleFormSubmit)}>
|
<form onSubmit={handleSubmit(handleFormSubmit)}>
|
||||||
<div
|
<div
|
||||||
className={twMerge(
|
className={twMerge(
|
||||||
@@ -227,9 +219,12 @@ export const SecretItem = memo(
|
|||||||
onCheckedChange={() => onToggleSecretSelect(secret.id)}
|
onCheckedChange={() => onToggleSecretSelect(secret.id)}
|
||||||
className={twMerge("ml-3 hidden group-hover:flex", isSelected && "flex")}
|
className={twMerge("ml-3 hidden group-hover:flex", isSelected && "flex")}
|
||||||
/>
|
/>
|
||||||
<FontAwesomeIcon
|
<FontAwesomeSymbol
|
||||||
icon={faKey}
|
className={twMerge(
|
||||||
className={twMerge("ml-3 block group-hover:hidden", isSelected && "hidden")}
|
"ml-3 block h-3.5 w-3.5 group-hover:hidden",
|
||||||
|
isSelected && "hidden"
|
||||||
|
)}
|
||||||
|
symbolName={FontAwesomeSpriteName.SecretKey}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex h-11 w-80 flex-shrink-0 items-center px-4 py-2">
|
<div className="flex h-11 w-80 flex-shrink-0 items-center px-4 py-2">
|
||||||
@@ -278,10 +273,12 @@ export const SecretItem = memo(
|
|||||||
key="secret-value"
|
key="secret-value"
|
||||||
control={control}
|
control={control}
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<SecretInput
|
<InfisicalSecretInput
|
||||||
isReadOnly={isReadOnly}
|
isReadOnly={isReadOnly}
|
||||||
key="secret-value"
|
key="secret-value"
|
||||||
isVisible={isVisible}
|
isVisible={isVisible}
|
||||||
|
environment={environment}
|
||||||
|
secretPath={secretPath}
|
||||||
{...field}
|
{...field}
|
||||||
containerClassName="py-1.5 rounded-md transition-all group-hover:mr-2"
|
containerClassName="py-1.5 rounded-md transition-all group-hover:mr-2"
|
||||||
/>
|
/>
|
||||||
@@ -297,7 +294,14 @@ export const SecretItem = memo(
|
|||||||
className="w-0 overflow-hidden p-0 group-hover:mr-2 group-hover:w-5"
|
className="w-0 overflow-hidden p-0 group-hover:mr-2 group-hover:w-5"
|
||||||
onClick={copyTokenToClipboard}
|
onClick={copyTokenToClipboard}
|
||||||
>
|
>
|
||||||
<FontAwesomeIcon icon={isSecValueCopied ? faCheck : faCopy} />
|
<FontAwesomeSymbol
|
||||||
|
className="h-3.5 w-3"
|
||||||
|
symbolName={
|
||||||
|
isSecValueCopied
|
||||||
|
? FontAwesomeSpriteName.Check
|
||||||
|
: FontAwesomeSpriteName.ClipboardCopy
|
||||||
|
}
|
||||||
|
/>
|
||||||
</IconButton>
|
</IconButton>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
<DropdownMenu>
|
<DropdownMenu>
|
||||||
@@ -318,7 +322,10 @@ export const SecretItem = memo(
|
|||||||
isDisabled={!isAllowed}
|
isDisabled={!isAllowed}
|
||||||
>
|
>
|
||||||
<Tooltip content="Tags">
|
<Tooltip content="Tags">
|
||||||
<FontAwesomeIcon icon={faTags} />
|
<FontAwesomeSymbol
|
||||||
|
className="h-3.5 w-3.5"
|
||||||
|
symbolName={FontAwesomeSpriteName.Tags}
|
||||||
|
/>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</IconButton>
|
</IconButton>
|
||||||
</DropdownMenuTrigger>
|
</DropdownMenuTrigger>
|
||||||
@@ -334,7 +341,14 @@ export const SecretItem = memo(
|
|||||||
<DropdownMenuItem
|
<DropdownMenuItem
|
||||||
onClick={() => handleTagSelect(tag)}
|
onClick={() => handleTagSelect(tag)}
|
||||||
key={`${secret.id}-${tagId}`}
|
key={`${secret.id}-${tagId}`}
|
||||||
icon={isTagSelected && <FontAwesomeIcon icon={faCheckCircle} />}
|
icon={
|
||||||
|
isTagSelected && (
|
||||||
|
<FontAwesomeSymbol
|
||||||
|
symbolName={FontAwesomeSpriteName.CheckedCircle}
|
||||||
|
className="h-3 w-3"
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
iconPos="right"
|
iconPos="right"
|
||||||
>
|
>
|
||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
@@ -353,7 +367,12 @@ export const SecretItem = memo(
|
|||||||
className="w-full"
|
className="w-full"
|
||||||
colorSchema="primary"
|
colorSchema="primary"
|
||||||
variant="outline_bg"
|
variant="outline_bg"
|
||||||
leftIcon={<FontAwesomeIcon icon={faTag} />}
|
leftIcon={
|
||||||
|
<FontAwesomeSymbol
|
||||||
|
symbolName={FontAwesomeSpriteName.Tags}
|
||||||
|
className="h-3 w-3"
|
||||||
|
/>
|
||||||
|
}
|
||||||
onClick={onCreateTag}
|
onClick={onCreateTag}
|
||||||
>
|
>
|
||||||
Create a tag
|
Create a tag
|
||||||
@@ -379,7 +398,10 @@ export const SecretItem = memo(
|
|||||||
isOverriden && "w-5 text-primary"
|
isOverriden && "w-5 text-primary"
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<FontAwesomeIcon icon={faCodeBranch} />
|
<FontAwesomeSymbol
|
||||||
|
symbolName={FontAwesomeSpriteName.Override}
|
||||||
|
className="h-3.5 w-3.5"
|
||||||
|
/>
|
||||||
</IconButton>
|
</IconButton>
|
||||||
)}
|
)}
|
||||||
</ProjectPermissionCan>
|
</ProjectPermissionCan>
|
||||||
@@ -393,6 +415,7 @@ export const SecretItem = memo(
|
|||||||
variant="plain"
|
variant="plain"
|
||||||
size="md"
|
size="md"
|
||||||
ariaLabel="add-reminder"
|
ariaLabel="add-reminder"
|
||||||
|
onClick={() => setCreateReminderFormOpen.on()}
|
||||||
>
|
>
|
||||||
<Tooltip
|
<Tooltip
|
||||||
content={
|
content={
|
||||||
@@ -404,9 +427,9 @@ export const SecretItem = memo(
|
|||||||
: "Reminder"
|
: "Reminder"
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<FontAwesomeIcon
|
<FontAwesomeSymbol
|
||||||
onClick={() => setCreateReminderFormOpen.on()}
|
className="h-3.5 w-3.5"
|
||||||
icon={faClock}
|
symbolName={FontAwesomeSpriteName.Clock}
|
||||||
/>
|
/>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</IconButton>
|
</IconButton>
|
||||||
@@ -430,7 +453,10 @@ export const SecretItem = memo(
|
|||||||
isDisabled={!isAllowed}
|
isDisabled={!isAllowed}
|
||||||
>
|
>
|
||||||
<Tooltip content="Comment">
|
<Tooltip content="Comment">
|
||||||
<FontAwesomeIcon icon={faComment} />
|
<FontAwesomeSymbol
|
||||||
|
className="h-3.5 w-3.5"
|
||||||
|
symbolName={FontAwesomeSpriteName.Comment}
|
||||||
|
/>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</IconButton>
|
</IconButton>
|
||||||
</PopoverTrigger>
|
</PopoverTrigger>
|
||||||
@@ -466,10 +492,13 @@ export const SecretItem = memo(
|
|||||||
ariaLabel="more"
|
ariaLabel="more"
|
||||||
variant="plain"
|
variant="plain"
|
||||||
size="md"
|
size="md"
|
||||||
className="p-0 opacity-0 group-hover:opacity-100"
|
className="p-0 opacity-0 group-hover:opacity-100 h-5 w-4"
|
||||||
onClick={() => onDetailViewSecret(secret)}
|
onClick={() => onDetailViewSecret(secret)}
|
||||||
>
|
>
|
||||||
<FontAwesomeIcon icon={faEllipsis} size="lg" />
|
<FontAwesomeSymbol
|
||||||
|
symbolName={FontAwesomeSpriteName.More}
|
||||||
|
className="h-5 w-4"
|
||||||
|
/>
|
||||||
</IconButton>
|
</IconButton>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
<ProjectPermissionCan
|
<ProjectPermissionCan
|
||||||
@@ -488,7 +517,10 @@ export const SecretItem = memo(
|
|||||||
onClick={() => onDeleteSecret(secret)}
|
onClick={() => onDeleteSecret(secret)}
|
||||||
isDisabled={!isAllowed}
|
isDisabled={!isAllowed}
|
||||||
>
|
>
|
||||||
<FontAwesomeIcon icon={faClose} size="lg" />
|
<FontAwesomeSymbol
|
||||||
|
symbolName={FontAwesomeSpriteName.Close}
|
||||||
|
className="h-5 w-4"
|
||||||
|
/>
|
||||||
</IconButton>
|
</IconButton>
|
||||||
)}
|
)}
|
||||||
</ProjectPermissionCan>
|
</ProjectPermissionCan>
|
||||||
@@ -516,10 +548,12 @@ export const SecretItem = memo(
|
|||||||
{isSubmitting ? (
|
{isSubmitting ? (
|
||||||
<Spinner className="m-0 h-4 w-4 p-0" />
|
<Spinner className="m-0 h-4 w-4 p-0" />
|
||||||
) : (
|
) : (
|
||||||
<FontAwesomeIcon
|
<FontAwesomeSymbol
|
||||||
icon={faCheck}
|
symbolName={FontAwesomeSpriteName.Close}
|
||||||
size="lg"
|
className={twMerge(
|
||||||
className={twMerge("text-primary", errors.key && "text-mineshaft-300")}
|
"h-4 w-4 text-primary",
|
||||||
|
errors.key && "text-mineshaft-300"
|
||||||
|
)}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</IconButton>
|
</IconButton>
|
||||||
@@ -536,7 +570,10 @@ export const SecretItem = memo(
|
|||||||
onClick={() => reset()}
|
onClick={() => reset()}
|
||||||
isDisabled={isSubmitting}
|
isDisabled={isSubmitting}
|
||||||
>
|
>
|
||||||
<FontAwesomeIcon icon={faClose} size="lg" />
|
<FontAwesomeSymbol
|
||||||
|
symbolName={FontAwesomeSpriteName.Close}
|
||||||
|
className="h-4 w-4 text-primary"
|
||||||
|
/>
|
||||||
</IconButton>
|
</IconButton>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { useCallback } from "react";
|
import { useCallback } from "react";
|
||||||
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
import { useQueryClient } from "@tanstack/react-query";
|
import { useQueryClient } from "@tanstack/react-query";
|
||||||
import { twMerge } from "tailwind-merge";
|
import { twMerge } from "tailwind-merge";
|
||||||
|
|
||||||
@@ -17,6 +18,7 @@ import { useSelectedSecretActions, useSelectedSecrets } from "../../SecretMainPa
|
|||||||
import { Filter, GroupBy, SortDir } from "../../SecretMainPage.types";
|
import { Filter, GroupBy, SortDir } from "../../SecretMainPage.types";
|
||||||
import { SecretDetailSidebar } from "./SecretDetaiSidebar";
|
import { SecretDetailSidebar } from "./SecretDetaiSidebar";
|
||||||
import { SecretItem } from "./SecretItem";
|
import { SecretItem } from "./SecretItem";
|
||||||
|
import { FontAwesomeSpriteSymbols } from "./SecretListView.utils";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
secrets?: DecryptedSecret[];
|
secrets?: DecryptedSecret[];
|
||||||
@@ -89,7 +91,6 @@ export const SecretListView = ({
|
|||||||
isVisible,
|
isVisible,
|
||||||
isProtectedBranch = false
|
isProtectedBranch = false
|
||||||
}: Props) => {
|
}: Props) => {
|
||||||
|
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
const { popUp, handlePopUpToggle, handlePopUpOpen, handlePopUpClose } = usePopUp([
|
const { popUp, handlePopUpToggle, handlePopUpOpen, handlePopUpClose } = usePopUp([
|
||||||
"deleteSecret",
|
"deleteSecret",
|
||||||
@@ -341,6 +342,13 @@ export const SecretListView = ({
|
|||||||
>
|
>
|
||||||
{namespace}
|
{namespace}
|
||||||
</div>
|
</div>
|
||||||
|
{FontAwesomeSpriteSymbols.map(({ icon, symbol }) => (
|
||||||
|
<FontAwesomeIcon
|
||||||
|
icon={icon}
|
||||||
|
symbol={symbol}
|
||||||
|
key={`font-awesome-svg-spritie-${symbol}`}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
{filteredSecrets.map((secret) => (
|
{filteredSecrets.map((secret) => (
|
||||||
<SecretItem
|
<SecretItem
|
||||||
environment={environment}
|
environment={environment}
|
||||||
|
|||||||
@@ -1,4 +1,16 @@
|
|||||||
/* eslint-disable no-nested-ternary */
|
/* eslint-disable no-nested-ternary */
|
||||||
|
import { faCheckCircle } from "@fortawesome/free-regular-svg-icons";
|
||||||
|
import {
|
||||||
|
faCheck,
|
||||||
|
faClock,
|
||||||
|
faClose,
|
||||||
|
faCodeBranch,
|
||||||
|
faComment,
|
||||||
|
faCopy,
|
||||||
|
faEllipsis,
|
||||||
|
faKey,
|
||||||
|
faTags
|
||||||
|
} from "@fortawesome/free-solid-svg-icons";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
export enum SecretActionType {
|
export enum SecretActionType {
|
||||||
@@ -41,3 +53,31 @@ export const formSchema = z.object({
|
|||||||
});
|
});
|
||||||
|
|
||||||
export type TFormSchema = z.infer<typeof formSchema>;
|
export type TFormSchema = z.infer<typeof formSchema>;
|
||||||
|
|
||||||
|
export enum FontAwesomeSpriteName {
|
||||||
|
SecretKey = "secret-key",
|
||||||
|
Check = "check",
|
||||||
|
ClipboardCopy = "clipboard-copy",
|
||||||
|
Tags = "secret-tags",
|
||||||
|
Clock = "reminder-clock",
|
||||||
|
Comment = "comment",
|
||||||
|
More = "more",
|
||||||
|
Override = "secret-override",
|
||||||
|
Close = "close",
|
||||||
|
CheckedCircle = "check-circle"
|
||||||
|
}
|
||||||
|
|
||||||
|
// this is an optimization technique
|
||||||
|
// https://docs.fontawesome.com/web/add-icons/svg-symbols
|
||||||
|
export const FontAwesomeSpriteSymbols = [
|
||||||
|
{ icon: faKey, symbol: FontAwesomeSpriteName.SecretKey },
|
||||||
|
{ icon: faCheck, symbol: FontAwesomeSpriteName.Check },
|
||||||
|
{ icon: faCopy, symbol: FontAwesomeSpriteName.ClipboardCopy },
|
||||||
|
{ icon: faTags, symbol: FontAwesomeSpriteName.Tags },
|
||||||
|
{ icon: faClock, symbol: FontAwesomeSpriteName.Clock },
|
||||||
|
{ icon: faComment, symbol: FontAwesomeSpriteName.Comment },
|
||||||
|
{ icon: faEllipsis, symbol: FontAwesomeSpriteName.More },
|
||||||
|
{ icon: faCodeBranch, symbol: FontAwesomeSpriteName.Override },
|
||||||
|
{ icon: faClose, symbol: FontAwesomeSpriteName.Close },
|
||||||
|
{ icon: faCheckCircle, symbol: FontAwesomeSpriteName.CheckedCircle }
|
||||||
|
];
|
||||||
|
|||||||
Reference in New Issue
Block a user